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;
}
@Override
public 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;
}
@Override
public 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;
}
@Override
public 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();
}
}
}
}
}
}