1、前言:
- 对象监射器:每一个类里面都会有一个monitor,锁的获取过程->进入抢占锁的过程->如果开起了偏向锁,会走偏向锁的撤销进行重新偏向->如果重新偏向失败的话,进行锁升级进入轻量级锁->然后锁膨胀到重量级锁,然后进行monitor的实现
- wait/notify一定是重量级锁来实现的
2、场景:
3、举例:生产者-消费者
通过一个共享的队列实现两个线程之间的通信
生产者:
public class Product implements Runnable{
private Queue<String> msg;
private int maxSize;
public Product(Queue<String> msg, int maxSize) {
this.msg = msg;
this.maxSize = maxSize;
}
@Override
public void run() {
int i=0;
while (true){
i++;
synchronized (msg){
while (msg.size()==maxSize){
try {
msg.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
//生产时间为1秒
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
msg.add(i+"");
System.out.println("生产者生产了一个消息:"+i);
//唤醒消费者
msg.notify();
}
}
}
}
消费者:
public class Consumer implements Runnable{
private Queue<String> msg;
private int maxSize;
public Consumer(Queue<String> msg, int maxSize) {
this.msg = msg;
this.maxSize = maxSize;
}
@Override
public void run() {
while (true){
synchronized (msg){
while (msg.isEmpty()){
//消息队列木得了
try {
msg.wait();//阻塞当前线程
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("消费者消费消息:"+msg.remove());
//生产者可能已经因为生产满了处于阻塞状态 所以也需要唤醒
msg.notify();
}
}
}
}
执行者
public class Test {
public static void main(String[] args) {
Queue<String> msg=new LinkedList<>();
int maxSize=5;
Product product=new Product(msg,maxSize);
Consumer consumer=new Consumer(msg,maxSize);
Thread t1=new Thread(product);
Thread t2=new Thread(consumer);
t2.start();
t1.start();
}
}
结果:
功能分析:生产者和消费者共用了一个共享变量msg,所以生产者和消费者中synchronized是同一把锁,实现了互斥;生产者(消费者)wait方法一定会释放锁进入wait阻塞状态,这样消费者(生产者)才有机会抢占到锁,但也可能是生产者(消费者)继续抢到锁,所以结果中出现了连续生产了5个消息,这是因为前五次生产者抢到了锁;notify会唤醒阻塞中的线程
首先两个队列:
- 等待队列:存储wait阻塞队列
- 同步队列:存储没有获得锁的block阻塞队列->下一次要进行锁抢占的队列
notify:释放锁,唤醒wait等待队列中的线程,将其放入同步队列中,进行重新锁抢占,没有抢占到留在同步队列中,抢占到的线程进入monitor,继续执行业务,执行完任务后notify,最后monitorexit释放重量级锁