wait原理
- entry set :入口集
 - wait set :等待集
 
- 进入入口集,等待对象释放锁
 - 对象锁持有者释放锁,当前线程获取锁
 - 当前线程在syncronized中调用wait方法,释放monitor锁,进入等待集
 - 其他线程调用notify或者notifyAll方法,使线程重新竞争monitor锁
 - 线程重新获取锁
 - 线程syncronized 方法或者代码块执行完毕,正常释放锁
 
常见问题
1、为什么 wait 必须在 synchronized 保护的同步代码中使用?
wait不加同步可能陷入永久等待或者死锁,同时wait 方法会释放 monitor 锁,这也要求我们必须首先进入到 synchronized 内持有这把锁。
2、为什么 wait/notify/notifyAll 被定义在 Object 类中,而 sleep 定义在 Thread 类中?
- 因为 Java 中每个对象都有一把称之为 monitor 监视器的锁,由于每个对象都可以上锁,这就要求在对象头中有一个用来保存锁信息的位置。这个锁是对象级别的,而非线程级别的,wait/notify/notifyAll 也都是锁级别的操作,它们的锁属于对象,所以把它们定义在 Object 类中是最合适,因为 Object 类是所有对象的父类。
 因为如果把 wait/notify/notifyAll 方法定义在 Thread 类中,会带来很大的局限性,比如一个线程可能持有多把锁,以便实现相互配合的复杂逻辑,假设此时 wait 方法定义在 Thread 类中,如何实现让一个线程持有多把锁呢?又如何明确线程等待的是哪把锁呢?既然我们是让当前线程去等待某个对象的锁,自然应该通过操作对象来实现,而不是操作线程。
3、wait/notify 和 sleep 方法的异同?
相同点:
它们都可以让线程阻塞。
- 它们都可以响应 interrupt 中断:在等待的过程中如果收到中断信号,都可以进行响应,并抛出 InterruptedException 异常。
 
不同点:
- wait 方法必须在 synchronized 保护的代码中使用,而 sleep 方法并没有这个要求。
 - 在同步代码中执行 sleep 方法时,并不会释放 monitor 锁,但执行 wait 方法时会主动释放 monitor 锁。
 - sleep 方法中会要求必须定义一个时间,时间到期后会主动恢复,而对于没有参数的 wait 方法而言,意味着永久等待,直到被中断或被唤醒才能恢复,它并不会主动恢复。
 - wait/notify 是 Object 类的方法,而 sleep 是 Thread 类的方法
 
生产者消费者模式
package com.imooc.thread_demo.prodconsumermodel;import java.util.Date;import java.util.LinkedList;import java.util.stream.IntStream;/*** @Author: zhangjx* @Date: 2020/9/9 23:25* @Description:*/public class ProducerConsumerModel {public static void main(String[] args) throws InterruptedException {StoargeQueue stoargeQueue = new StoargeQueue();Producer producer = new Producer(stoargeQueue);Consumer consumer = new Consumer(stoargeQueue);new Thread(producer).start();Thread.sleep(100);new Thread(consumer).start();}static class Producer implements Runnable{private StoargeQueue stoargeQueue;Producer(StoargeQueue stoargeQueue){this.stoargeQueue = stoargeQueue;}@Overridepublic void run() {IntStream.range(0,20).forEach(e -> {stoargeQueue.put();//睡眠1ms 让消费者竞争minitor锁try {Thread.sleep(1);} catch (InterruptedException interruptedException) {interruptedException.printStackTrace();}});System.out.println("生产者退出");}}static class Consumer implements Runnable{private StoargeQueue stoargeQueue;Consumer(StoargeQueue stoargeQueue){this.stoargeQueue = stoargeQueue;}@Overridepublic void run() {IntStream.range(0,20).forEach(e -> {stoargeQueue.get();try {Thread.sleep(1);} catch (InterruptedException interruptedException) {interruptedException.printStackTrace();}});System.out.println("消费者退出");}}static class StoargeQueue {private int maxSize = 3;private LinkedList<String> list = new LinkedList<>();public StoargeQueue() {}public synchronized void put(){while(list.size() == maxSize){System.out.println("存储队列已满,生产者停止生产");try {wait();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("生产者被唤醒,继续生产");}list.add(String.valueOf(new Date()));System.out.println("生产者生产====================");notify();}public synchronized void get(){while (list.size() == 0){System.out.println("存储队列已空,消费者停止消费");notify();try {wait();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("消费者被唤醒,继续消费");}list.poll();System.out.println("消费者消费====================");}}}
输出
"C:\Program Files\Java\jdk1.8.0_171\bin\java.exe" "-javaagent:D:\idea2020\IntelliJ IDEA 2020.1.2\lib\idea_rt.jar=64225:D:\idea2020\IntelliJ IDEA 2020.1.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_171\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_171\jre\lib\rt.jar;C:\Users\z\Desktop\mooc\多线程\thread_demo\target\classes;E:\repository\org\springframework\boot\spring-boot-starter\2.2.2.RELEASE\spring-boot-starter-2.2.2.RELEASE.jar;E:\repository\org\springframework\boot\spring-boot\2.2.2.RELEASE\spring-boot-2.2.2.RELEASE.jar;E:\repository\org\springframework\spring-context\5.2.2.RELEASE\spring-context-5.2.2.RELEASE.jar;E:\repository\org\springframework\spring-aop\5.2.2.RELEASE\spring-aop-5.2.2.RELEASE.jar;E:\repository\org\springframework\spring-beans\5.2.2.RELEASE\spring-beans-5.2.2.RELEASE.jar;E:\repository\org\springframework\spring-expression\5.2.2.RELEASE\spring-expression-5.2.2.RELEASE.jar;E:\repository\org\springframework\boot\spring-boot-autoconfigure\2.2.2.RELEASE\spring-boot-autoconfigure-2.2.2.RELEASE.jar;E:\repository\org\springframework\boot\spring-boot-starter-logging\2.2.2.RELEASE\spring-boot-starter-logging-2.2.2.RELEASE.jar;E:\repository\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;E:\repository\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;E:\repository\org\apache\logging\log4j\log4j-to-slf4j\2.12.1\log4j-to-slf4j-2.12.1.jar;E:\repository\org\apache\logging\log4j\log4j-api\2.12.1\log4j-api-2.12.1.jar;E:\repository\org\slf4j\jul-to-slf4j\1.7.29\jul-to-slf4j-1.7.29.jar;E:\repository\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;E:\repository\org\springframework\spring-core\5.2.2.RELEASE\spring-core-5.2.2.RELEASE.jar;E:\repository\org\springframework\spring-jcl\5.2.2.RELEASE\spring-jcl-5.2.2.RELEASE.jar;E:\repository\org\yaml\snakeyaml\1.25\snakeyaml-1.25.jar;E:\repository\org\springframework\boot\spring-boot-devtools\2.2.2.RELEASE\spring-boot-devtools-2.2.2.RELEASE.jar;E:\repository\org\springframework\boot\spring-boot-configuration-processor\2.2.2.RELEASE\spring-boot-configuration-processor-2.2.2.RELEASE.jar;E:\repository\org\projectlombok\lombok\1.18.10\lombok-1.18.10.jar;E:\repository\org\slf4j\slf4j-api\1.7.29\slf4j-api-1.7.29.jar" com.imooc.thread_demo.prodconsumermodel.ProducerConsumerModel生产者生产====================生产者生产====================生产者生产====================存储队列已满,生产者停止生产消费者消费====================消费者消费====================消费者消费====================存储队列已空,消费者停止消费生产者被唤醒,继续生产生产者生产====================消费者被唤醒,继续消费消费者消费====================生产者生产====================消费者消费====================存储队列已空,消费者停止消费生产者生产====================消费者被唤醒,继续消费消费者消费====================存储队列已空,消费者停止消费生产者生产====================消费者被唤醒,继续消费消费者消费====================生产者生产====================消费者消费====================存储队列已空,消费者停止消费生产者生产====================消费者被唤醒,继续消费消费者消费====================生产者生产====================消费者消费====================生产者生产====================消费者消费====================生产者生产====================消费者消费====================生产者生产====================消费者消费====================存储队列已空,消费者停止消费生产者生产====================消费者被唤醒,继续消费消费者消费====================存储队列已空,消费者停止消费生产者生产====================消费者被唤醒,继续消费消费者消费====================生产者生产====================消费者消费====================生产者生产====================消费者消费====================生产者生产====================消费者消费====================生产者生产====================消费者消费====================生产者生产====================消费者消费====================消费者退出生产者退出Process finished with exit code 0
两线程交替打印
package com.imooc.thread_demo.waitnotify;/*** @Author: zhangjx* @Date: 2020/9/10 16:02* @Description: 两个线程交替打印*/public class WaitNotifyTwoThreadRoundPrint {private static int count = 0;public static void main(String[] args) throws InterruptedException {Object lock = new Object();ThreadPrint threadA = new ThreadPrint(lock);ThreadPrint threadB = new ThreadPrint(lock);new Thread(threadA).start();Thread.sleep(100);new Thread(threadB).start();}static class ThreadPrint implements Runnable{private Object lock;ThreadPrint(Object lock){this.lock = lock;}@Overridepublic void run() {while(count <= 100){synchronized (lock){System.out.println(Thread.currentThread().getName() + ":" + count++);lock.notify();try {if(count <= 100){lock.wait();}} catch (InterruptedException e) {e.printStackTrace();}}}}}}
