Android 当中的多线程之 wait、sleep、notify、join、yield

匿名 (未验证) 提交于 2019-12-03 00:37:01

Android 当中的多线程实际上就是 Java SE 中的多线程,只是为了方便使用,Android 封装了一些类,比如 AsyncTask, HandlerThread等。今天我们总结一下 Android 当中的多线程基础知识。

对于 Android 多线程,我们最早学习到的都是 Thread 和 Runnable ,通常我们使用如下代码来开启一个新的线程:

public void startNewThread() {     new Thread(){         @Override         public void run() {             super.run();             // 执行耗时操作         }     }.start(); }

或者是

public void startNewThreadWithRunnable() {     new Thread(new Runnable() {         @Override         public void run() {             // 执行耗时操作         }     }).start(); }

实际上这两种写法的差别不大,那么 Thread 和 Runnable 有什么区别呢?

实际上 Thread 也是一个 Runnable,它实现了 Runnable 接口,内部包含了一个 Runnable 类型的 target 表示要在这个子线程执行的操作

public class Thread implements Runnable {     /* What will be run. */     private Runnable target;      /* The group of this thread */     private ThreadGroup group;      public Thread() {         init(null, null, "Thread-" + nextThreadNum(), 0);     }       * @param  target      *         the object whose run method is invoked when this thread      *         is started. If null, this classes run method does      *         nothing.      public Thread(Runnable target) {             init(null, target, "Thread-" + nextThreadNum(), 0);     }      // 初始化 Thread 并且将该 Thread 添加到 ThreadGroup 中     private void init(ThreadGroup g, Runnable target, String name, long stackSize) {         Thread parent = currentThread();         if (g == null) {             // 如果当前 ThreadGroup 参数为 null,则获取当前线程的线程组             g = parent.getThreadGroup();         }         // 添加到线程组的未执行 list         g.addUnstarted();         this.group = g;         // 设置带执行 Runnable target         this.target = target;         this.priority = parent.getPriority();         this.daemon = parent.isDaemon();         setName(name);          init2(parent);          /* Stash the specified stack size in case the VM cares */         this.stackSize = stackSize;         tid = nextThreadID();     }   public synchronized void start() {          /* Notify the group that this thread is about to be started          * so that it can be added to the group's list of threads          * and the group's unstarted count can be decremented. */         group.add(this);          started = false;         try {             // 调用 native 函数启动新的线程             nativeCreate(this, stackSize, daemon);             started = true;         } finally {             try {                 if (!started) {                     group.threadStartFailed(this);                 }             } catch (Throwable ignore) {                 /* do nothing. If start0 threw a Throwable then                   it will be passed up the call stack */             }         }     } }

由此可知,实际上最终被线程执行的任务是 Runnable , 而非 Thread 。Thread 只是对 Runnable 进行了一定的封装,并且通过一些状态对 Thread 进行管理和调度。

Runnable 接口定义了可执行的任务,它只有一个无返回值的 run() 方法

@FunctionalInterface public interface Runnable {     public abstract void run(); }

当启动一个线程的时候,如果 target 不为空,就执行 Runnable 的 run() 方法,否则执行 Thread 自身的 run() 方法

方法名 作用
wait() 当一个线程执行到 wait()方法时,它就进入到一个和该对象现关的等待池中,同时失去(释放)了对象的机锁,使得其他线程可以访问。用户可以使用 notify、notifyAll或者指定睡眠时间来唤醒当前等待池的线程。 注意:wait()、notify()、nofityAll() 必须放在 synchronized block 中,否则会抛出异常
sleep 该函数是 Thread 的静态函数,作用是使调用线程进入睡眠状态。因为 sleep()是 Thread 类的 static 方法,因此它不能改变对象的机锁。所以,当在一个 Synchronized block 中调用 Sleep()方法时,线程虽然休眠了,但是对象的机锁没有被释放,其他线程无法访问这个对象(即使睡着也持有对象锁)。
join 等待目标线程执行完成之后再继续执行
yield 线程礼让。目标线程由运行状态转换为就绪状态,也就是让出执行权限,让其他线程得以优先执行,但其他线程能否优先执行是未知的。

wait \ notify 、notifyAll 的使用

private void waitAndNotify() {     Log.e(TAG, "waitAndNotify: \"主线程运行\"" );     new WaitThread().start();     long startTime = System.currentTimeMillis();      synchronized (sObject) {         try {             Log.e(TAG, "waitAndNotify: \"主线程等待\"" );             // 让主线程进入等待状态             sObject.wait();         } catch (InterruptedException e) {             e.printStackTrace();         }     }     long waitTime = System.currentTimeMillis()- startTime;     Log.e(TAG, "waitAndNotify: 主线程等待耗时 "+ waitTime+" ms");  }  static class WaitThread extends Thread {     @Override     public void run() {         super.run();         try {             synchronized (sObject) {                 Thread.sleep(5000);                 // 唤醒 sObject 所在的主线程                 sObject.notifyAll();             }         } catch (InterruptedException e) {             e.printStackTrace();         }     } }

在 waitAndNotify() 方法中,启动了一个 WaitThread 线程,在该线程中调用 sleep() 方法使线程睡眠 5 秒。线程启动以后,在主线程调用 sObject 的 wait() 方法,使主线程进入等待状态,此时将不会继续执行,直到 WaitThread 从 5 秒睡眠中醒过来,调用 sObject 的 wait() 方法唤醒主线程,程序继续往下走。因此得到如下结果:

    waitAndNotify: "主线程运行"     waitAndNotify: "主线程等待"     waitAndNotify: 主线程等待耗时 5000 ms

wait、notify 通常用于等待机制的实现,当条件未满足的时候,调用 wait 进入等待状态,一旦条件满足,调用 notify、notifyAll 唤醒等待的线程继续执行。

join的使用
join: 阻塞当前调用 join 方法所在线程,直到接收线程执行完毕之后再继续。

private void joinTest() {         Thread thread1 = new Thread("thread_1"){             @Override             public void run() {                 super.run();                 try {                     Thread.sleep(2000);                 } catch (InterruptedException e) {                     e.printStackTrace();                 }                 Log.e(TAG, "run: thread_1" );             }         };          Thread thread2 = new Thread("thread_2"){             @Override             public void run() {                 super.run();                 try {                     Thread.sleep(2000);                 } catch (InterruptedException e) {                     e.printStackTrace();                 }                 Log.e(TAG, "run: thread2");             }         };          Log.e(TAG, "joinTest: 主线程启动");         try {             thread1.start();             thread1.join();             thread2.start();             thread2.join();         } catch (InterruptedException e) {             e.printStackTrace();         }         Log.e(TAG, "joinTest: 主线程继续执行" );     }

执行结果:

MainActivity: joinTest: 主线程启动 MainActivity: run: thread_1 // 主线程等待线程1执行完成 MainActivity: run: thread2  // 主线程等待线程2执行完成 MainActivity: joinTest: 主线程继续执行

yield使用
yield:调用该方法的线程让出执行时间给其他已经准备就绪的线程。我们知道,线程的执行是有时间片的,每个线程轮流占用 CPU 固定的时间片,执行周期到了之后,就让出执行权给其他的线程。 yield 就是主动让出线程的执行权给其他的线程,其他线程能否得到优先执行权就得看各个线程的状态了。

    static class YieldThread extends Thread{          public YieldThread(String name) {             super(name);         }          @Override         public void run() {             super.run();             for (int i = 0;i<5;i++) {                 Log.e("yieldThread", getName()+", 优先级为:"+getPriority()+"  ----->"+ i );                 if (i % 2 == 0) {                     // 让出线程执行权                     yield();                 }             }         }     }     ...     private void yieldTest() {        YieldThread yieldThread1 = new YieldThread("thread_1");        YieldThread yieldThread2 = new YieldThread("thread_2");        yieldThread1.start();        yieldThread2.start();    }

执行结果:

thread_1, 优先级为:5  ----->0 thread_2, 优先级为:5  ----->0 thread_1, 优先级为:5  ----->1 thread_1, 优先级为:5  ----->2 thread_2, 优先级为:5  ----->1 thread_2, 优先级为:5  ----->2 thread_1, 优先级为:5  ----->3 thread_1, 优先级为:5  ----->4 thread_2, 优先级为:5  ----->3 thread_2, 优先级为:5  ----->4 

线程1首先执行,执行完一次以后,让出执行权,线程2继续执行,执行完一次以后让出执行权,线程1继续执行….

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!