Thread 的 wait、sleep、join、yied 理解
一.查看API
sleep是Thread类的方法,导致此线程暂停执行指定时间,给其他线程执行机会,但是依然保持着监控状态,过了指定时间会自动恢复,调用sleep方法不会释放锁对象。
当调用sleep方法后,当前线程进入阻塞状态。目的是让出CPU给其他线程运行的机会。但是由于sleep方法不会释放锁对象,所以在一个同步代码块中调用这个方法后,线程虽然休眠了,但其他线程无法访问它的锁对象。这是因为sleep方法拥有CPU的执行权,它可以自动醒来无需唤醒。而当sleep()结束指定休眠时间后,这个线程不一定立即执行,因为此时其他线程可能正在运行。
wait方法是Object类里的方法,当一个线程执行到wait()方法时,它就进入到一个和该对象相关的等待池中,同时释放了锁对象,等待期间可以调用里面的同步方法,其他线程可以访问,等待时不拥有CPU的执行权,否则其他线程无法获取执行权。当一个线程执行了wait方法后,必须调用notify或者notifyAll方法才能唤醒,而且是随机唤醒,若是被其他线程抢到了CPU执行权,该线程会继续进入等待状态。由于锁对象可以时任意对象,所以wait方法必须定义在Object类中,因为Obeject类是所有类的基类。
二.是否可以传入参数
sleep()方法必须传入参数,参数就是休眠时间,时间到了就会自动醒来。
wait()方法可以传入参数也可以不传入参数,传入参数就是在参数结束的时间后开始等待,不穿如参数就是直接等待。
三.是否需要捕获异常
sleep方法必须要捕获异常,而wait方法不需要捕获异常。sleep方法属于Thread类中方法,表示让一个线程进入睡眠状态,等待一定的时间之后,自动醒来进入到可运行状态,不会马上进入运行状态,因为线程调度机制恢复线程的运行也需要时间,一个线程对象调用了sleep方法之后,并不会释放他所持有的所有对象锁,所以也就不会影响其他进程对象的运行。但在sleep的过程中过程中有可能被其他对象调用它的interrupt(),产生InterruptedException异常,如果你的程序不捕获这个异常,线程就会异常终止,进入TERMINATED状态,如果你的程序捕获了这个异常,那么程序就会继续执行catch语句块(可能还有finally语句块)以及以后的代码。
wait属于Object的成员方法,一旦一个对象调用了wait方法,必须要采用notify()和notifyAll()方法唤醒该进程;如果线程拥有某个或某些对象的同步锁,那么在调用了wait()后,这个线程就会释放它持有的所有同步资源,而不限于这个被调用了wait()方法的对象。wait()方法也同样会在wait的过程中有可能被其他对象调用interrupt()方法而产生。
四.作用范围
wait、notify和notifyAll方法只能在同步方法或者同步代码块中使用,而sleep方法可以在任何地方使用。但是注意sleep是静态方法,也就是说它只对当前对象有效。通过对象名.sleep()想让该对象线程进入休眠是无效的,它只会让当前线程进入休眠。
五.调用者的区别
首先为什么wait、notify和notifyAll方法要和synchronized关键字一起使用?
因为wait方法是使一个线程进入等待状态,并且释放其所持有的锁对象,notify方法是通知等待该锁对象的线程重新获得锁对象,然而如果没有获得锁对象,wait方法和notify方法都是没有意义的,因此必须先获得锁对象再对锁对象进行进一步操作于是才要把wait方法和notify方法写到同步方法和同步代码块中了。
由此可知,wait和notify、notifyAll方法是由确定的对象即锁对象来调用的,锁对象就像一个传话的人,他对某个线程说停下来等待,然后对另一个线程说你可以执行了(实质上是被捕获了),这一过程是线程通信。sleep方法是让某个线程暂停运行一段时间,其控制范围是由当前线程决定,运行的主动权是由当前线程来控制(拥有CPU的执行权)。
其实两者的区别都是让线程暂停运行一段时间,但本质的区别一个是线程的运行状态控制,一个是线程间的通信。
函数名 | 作用 |
wait |
当一个线程执行到wait()方法时,它就进入到一个和该对象相关的等待池中,同时释放了对象的锁,使得其他线程可以访问。用户可以使用notify、notifyAll或者指定睡眠时间来唤醒当前等待池中的线程。 注意:wait()、notify()、notifyAll()必须放在synchronized block中,否则会抛出异常
wait()方法可以传入参数也可以不传入参数,传入参数就是在参数结束的时间后开始等待,不穿如参数就是直接等待。 wait方法不需要捕获异常 |
sleep |
该函数是Thread的静态函数,作用是使调用线程进入睡眠状态。因为sleep()是Thread类的Static方法,因此他不能改变对象的锁。所以,当在一个Sychronized块中调用sleep()方法,线程虽然休眠了,但是对象的锁并没有释放,其他线程无法访问这个对象(即使睡着也持有对象锁)
sleep()方法必须传入参数,参数就是休眠时间,时间到了就会自动醒来。 sleep方法必须要捕获异常 |
join | 等待目标线程执行完成之后再继续执行 |
yield | 线程礼让。目标线程由运行状态转换为就绪状态,也就是让出执行权限,让其他线程得以优先执行,但其他线程能否优先执行是未知数 |
package com.adao.thread.yeild; public class YieldThread extends Thread { public YieldThread(String name) { super(name); } public synchronized void run() { for (int i = 0; i < 5; i++) { System.out.println(String.format("%s [%d] ---> %d", this.getName(), this.getPriority(), i)); // 当i为2时调用当前线程的yield函数 // if (i == 2) { // Thread.yield(); // } } } public static void main(String[] args) { YieldThread t1 = new YieldThread("thread-1"); YieldThread t2 = new YieldThread("thread-2"); t1.start(); t2.start(); } }
结果:
取消屏蔽:
我们看到执行到的thread-2 等于2之前交替执行,等于2时,thread-2让出执行时间,执行thread-1,thread-1执行到2时又将执行权让出
JOIN
package com.adao.thread.yeild; public class Worker extends Thread { public Worker(String name) { super(name); } @Override public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("work in " + getName()); } public static void main(String[] args) { Worker worker1 = new Worker("work-1"); Worker worker2 = new Worker("work-2"); worker1.start(); System.out.println("启动线程1"); try { // 调用work1的join函数,主线程会阻塞直到work1执行完成 worker1.join(); System.out.println("启动线程2"); // 再启动线程2,并且调用线程2的join函数,主线程会阻塞直到worker2执行完成 worker2.start(); worker2.join(); } catch (Exception ignored) { } System.out.println("主线程继续执行"); } }
Wait:
package com.adao.thread.yeild; public class Wait { private static Object sLockObject = new Object(); static void waitAndNotifyAll() { System.out.println("主线程运行"); Thread thread = new WaitThread(); thread.start(); long startTime = System.currentTimeMillis(); try { synchronized (sLockObject) { System.out.println("主线程等待"); sLockObject.wait(); } } catch (Exception ignored) { } long timsMs = (System.currentTimeMillis() - startTime); System.out.println("主线程继续-->等待耗时:" + timsMs + "ms"); } static class WaitThread extends Thread { @Override public void run() { try { synchronized (sLockObject) { Thread.sleep(3000); sLockObject.notifyAll(); } } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { Wait.waitAndNotifyAll(); } }