四种情况下会被唤醒
另一个线程调用这个对象的notify()方法且刚好被唤醒的是本线程。另一个线程调用这个对象的notifyAll()过了wait(long timeout)规定的超时时间,如果传入0就是永久等待。线程自身调用了interrupt()notify会唤醒单个正在等待某对象monitor的线程,如果有多个线程都在等待,它只会唤醒一个,具体唤醒的选择是任意的,java对此没有明确规范,JVM可以拥有自己的实现,对此有一定的自由裁量权,而notify和wait都得在synchronize保护的代码块或者方法中执行
/** * 描述: 展示wait和notify的基本用法 1. 研究代码执行顺序 2. 证明wait释放锁 */ public class Wait { public static Object object = new Object(); static class Thread1 extends Thread { @Override public void run() { synchronized (object) { System.out.println(Thread.currentThread().getName() + "开始执行了"); try { object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程" + Thread.currentThread().getName() + "获取到了锁。"); } } } static class Thread2 extends Thread { @Override public void run() { synchronized (object) { object.notify(); System.out.println("线程" + Thread.currentThread().getName() + "调用了notify()"); } } } public static void main(String[] args) throws InterruptedException { Thread1 thread1 = new Thread1(); Thread2 thread2 = new Thread2(); thread1.start(); Thread.sleep(200); thread2.start(); } } /** * 描述: 3个线程,线程1和线程2首先被阻塞,线程3唤醒它们。notify, notifyAll。 start先执行不代表线程先启动。 */ public class WaitNotifyAll implements Runnable { private static final Object resourceA = new Object(); public static void main(String[] args) throws InterruptedException { Runnable r = new WaitNotifyAll(); Thread threadA = new Thread(r); Thread threadB = new Thread(r); Thread threadC = new Thread(new Runnable() { @Override public void run() { synchronized (resourceA) { resourceA.notifyAll(); // resourceA.notify(); System.out.println("ThreadC notified."); } } }); threadA.start(); threadB.start(); // Thread.sleep(200); threadC.start(); } @Override public void run() { synchronized (resourceA) { System.out.println(Thread.currentThread().getName()+" got resourceA lock."); try { System.out.println(Thread.currentThread().getName()+" waits to start."); resourceA.wait(); System.out.println(Thread.currentThread().getName()+"'s waiting to end."); } catch (InterruptedException e) { e.printStackTrace(); } } } } /** 1. 描述: 证明wait只释放当前的那把锁 */ public class WaitNotifyReleaseOwnMonitor { private static volatile Object resourceA = new Object(); private static volatile Object resourceB = new Object(); public static void main(String[] args) { Thread thread1 = new Thread(new Runnable() { @Override public void run() { synchronized (resourceA) { System.out.println("ThreadA got resourceA lock."); synchronized (resourceB) { System.out.println("ThreadA got resourceB lock."); try { System.out.println("ThreadA releases resourceA lock."); resourceA.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }); Thread thread2 = new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (resourceA) { System.out.println("ThreadB got resourceA lock."); System.out.println("ThreadB tries to resourceB lock."); synchronized (resourceB) { System.out.println("ThreadB got resourceB lock."); } } } }); thread1.start(); thread2.start(); } }Entry Set 入口集 Wait Set 等待集
用程序实现两个线程交替打印0~100的奇偶数
基本思路 synchronize
/** * 描述: 两个线程交替打印0~100的奇偶数,用synchronized关键字实现 */ public class WaitNotifyPrintOddEvenSyn { private static int count; private static final Object lock = new Object(); //新建2个线程 //1个只处理偶数,第二个只处理奇数(用位运算) //用synchronized来通信 public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { while (count < 100) { synchronized (lock) { if ((count & 1) == 0) { System.out.println(Thread.currentThread().getName() + ":" + count++); } } } } }, "偶数").start(); new Thread(new Runnable() { @Override public void run() { while (count < 100) { synchronized (lock) { if ((count & 1) == 1) { System.out.println(Thread.currentThread().getName() + ":" + count++); } } } } }, "奇数").start(); } }用wait和notify减少废操作
/** * 描述: 两个线程交替打印0~100的奇偶数,用wait和notify */ public class WaitNotifyPrintOddEveWait { private static int count = 0; private static final Object lock = new Object(); public static void main(String[] args) { new Thread(new TurningRunner(), "偶数").start(); new Thread(new TurningRunner(), "奇数").start(); } //1. 拿到锁,我们就打印 //2. 打印完,唤醒其他线程,自己就休眠 static class TurningRunner implements Runnable { @Override public void run() { while (count <= 100) { synchronized (lock) { //拿到锁就打印 System.out.println(Thread.currentThread().getName() + ":" + count++); lock.notify(); if (count <= 100) { try { //如果任务还没结束,就让出当前的锁,并休眠 lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } } }手写生产者消费者设计模式 上文已写
为什么wait()需要在同步代码块使用而sleep()不需要 为了让通信变得可靠,防止死锁和永久等待的发生,因为如果我们不把wait和notify都放在代码块里面的话,那么很有可能是执行wait之前,线程突然切换,切换到具有notify的一个线程,因为没有synchronize保护,随时都可以切过去,这样对面的第二个线程就吧程序执行完毕了,会导致进入wait()后,没有notify能唤醒,就会永久等待或者死锁,而sleep不存在这样的问题。
为什么线程通信的方法wait(),notify(),notifyAll()被定义在Object里面,而sleep定义在Thread里面 因为wait(),notify(),notifyAll(),是锁级别的操作,而锁是属于某个对象的,所以这三个方法被定义在Object里面。
wait方法是属于Object对象的,那调用Thread.wait()会怎么样? Thread也是继承Object的,但是对于Thread类很特殊,线程退出时会自动调用notify(),这样设计的整个流程都会受到影响。
notifyAll后,所有线程都去再次抢夺锁,如果某线程抢夺失败会如何 没有抢到锁的线程会进行等待,直到拿到锁。
作用:让线程在预期的时间执行,其他时候不要占用CPU资源 sleep方法不释放锁:
包括synchrinized 和 lock和wait不同 /** 1. 展示线程sleep的时候不释放synchronized的monitor,等sleep时间到了以后,正常结束后才释放锁 */ public class SleepDontReleaseMonitor implements Runnable { public static void main(String[] args) { SleepDontReleaseMonitor sleepDontReleaseMonitor = new SleepDontReleaseMonitor(); new Thread(sleepDontReleaseMonitor).start(); new Thread(sleepDontReleaseMonitor).start(); } @Override public void run() { syn(); } private synchronized void syn() { System.out.println("线程" + Thread.currentThread().getName() + "获取到了monitor。"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程" + Thread.currentThread().getName() + "退出了同步代码块"); } }sleep方法响应中断
抛出InterruptedException清除中断状态 /** * 描述: 每个1秒钟输出当前时间,被中断,观察。 * Thread.sleep() * TimeUnit.SECONDS.sleep() */ public class SleepInterrupted implements Runnable{ public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new SleepInterrupted()); thread.start(); Thread.sleep(6500); thread.interrupt(); } @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(new Date()); try { TimeUnit.HOURS.sleep(3); TimeUnit.MINUTES.sleep(25); TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { System.out.println("我被中断了!"); e.printStackTrace(); } } } }sleep方法可以让线程进入Waiting状态,并且不占用CPU资源,但是不释放锁,直到规定时间后再执行,休眠期间如果被中断,会抛出异常并清除中断状态。
sleep,wait/nofity异同(方法属于哪个对象?线程状态怎么切换?)
相同 阻塞:都会让线程进入阻塞状态 响应中断:即使休眠期间也会响应中断,抛出异常
不同 同步方法中:wait和notify必须在同步方法中执行(线程安全,防止死锁和永久等待),sleep不需要 释放锁: wait会释放锁,sleep不会 指定时间:sleep必须传参,wait如果不传参会直到自己被唤醒。 所属类
join注意点 CountDownLatch 或CyclicBarrier类 相同效果的实现类
join原理 源码:
public final synchronized void join(long millis) throws InterruptedException { long base = System.currentTimeMillis(); long now = 0; if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (millis == 0) { while (isAlive()) { wait(0); } } else { while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay); now = System.currentTimeMillis() - base; } } }写出join的替代方法
/** * 描述: 通过了解join原理,分析出join的代替写法 */ public class JoinPrinciple { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "执行完毕"); } }); thread.start(); System.out.println("开始等待子线程运行完毕"); thread.join(); // synchronized (thread) { // thread.wait(); // } System.out.println("所有子线程执行完毕"); } }在join期间,线程处于哪种线程状态?
join期间线程会处于waiting的状态