多线程的通知机制
Posted Melody袁
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程的通知机制相关的知识,希望对你有一定的参考价值。
典型应用场景–阻塞队列
生产者(Productor)—放入元素
消费者(Consumer)—取出元素
生产者放元素时,队列满了,该怎么办?
消费者取元素时,队列空了,该怎么办?
没有采用阻塞队列之前采用一种轮询模式,定期去问(性能浪费。实时性低)。
因此出现了另一种模式,通知模式,留个联系方式,状态改变以后会有人找你。
java中如何提供通知模式的?
Object的三个普通方法:
wait();导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法。
notify();唤醒正在等待对象监视器的单个线程。
notifyAll();唤醒正在等待对象监视器的所有线程。
由于Obejct是java中所有类的祖先类,所以,所有类的对象,都具备这三个方法。
注意事项:
- 要在某个对象(o)上进行wait/notify/notifyAll,首先必须持有这个对象的锁
import java.util.concurrent.TimeUnit;
class MyThread extends Thread
// boolean condation = true;
@Override
public void run()
try
TimeUnit.SECONDS.sleep(5);
catch (InterruptedException e)
e.printStackTrace();
synchronized (ThreadDemo.o)
ThreadDemo.o.notify();
public class ThreadDemo
static Object o = new Object();
public static void main(String[] args) throws InterruptedException
MyThread thread = new MyThread();
thread.start();
System.out.println("准备开始wait");
synchronized (o)
o.wait();
System.out.println("从wait中醒来");
- 先notify再wait可以收到之前的通知信号吗?
无法收到,通知信号状态不保留。 - 在调用wait的线程真正放弃CPU之前隐含着一次释放锁的过程,释放o这个锁,而不是所有的锁。
从wait返回之前,隐含着需要重新请求锁(o这个锁)。
package 阻塞队列.demo2;
/*
通过jconsole查看线程持有锁的状态
*/
import java.util.*;
public class ThreadDemo
public static void main(String[] args) throws InterruptedException
Scanner s = new Scanner(System.in);
synchronized (s)
s.nextLine();
System.out.println(1);//等待输入,不输入没有任何返回
s.wait(20_0000);
System.out.println(2);
s.nextLine();
System.out.println(3);
Thread.sleep(5000)和o.wait(5000)的区别
根本区别:
语义:
sleep休眠5秒(保证一定能够休眠5秒)
wait等待通知,最多等5秒(不保证一定能休眠够5秒,一旦被通知了,可以立即醒来)
表象区别:
sleep的调用不需要持有锁
wait休眠过程中,会释放锁,但只会释放o这个对象锁,其他持有的锁不释放。
package 阻塞队列.demo2;
import java.util.*;
class Lock1
class Lock2
public class ThreadDemo2
public static void main(String[] args) throws InterruptedException
Object lock1 = new Lock1();
Object lock2 = new Lock2();
Scanner s = new Scanner(System.in);
synchronized (lock1)
synchronized (lock2)
s.nextLine(); //lock1 & lock2
System.out.println(1);
// lock2.wait(5_0000);//lock1
Thread.sleep(5_0000);//lock1 & lock2
System.out.println(2);
s.nextLine(); //lock1 & lock2
wait被唤醒的条件
- 有其他线程调用notify,并且wait的线程被选中唤醒(随机唤醒)
- 有其他线程调用notifyAll
- 有其他线程执行类似interrupt()操作,通知线程停止,并抛出InterruptedException
- wait(timeout)达到超时时间时也会被唤醒
- 在某些系统上,由于JVM实现的原因,可能会出现虚假唤醒(上述的四个条件都没有发生的情况下)
一般调用wait方法,我们总是期待着某些条件。
所以一般的编码规范都是把wait放入一个循环中,循环的条件就是对我们的期望条件进行判断。
从wait中醒来时,无法保证条件符合我们的预期了。
import java.util.concurrent.TimeUnit;
class MyThread extends Thread
@Override
public void run()
// 模拟算的时间比较久
try
TimeUnit.SECONDS.sleep(5);
catch (InterruptedException exc)
exc.printStackTrace();
synchronized (ThreadDemo.class)
ThreadDemo.result = 100;
ThreadDemo.class.notify();
public class ThreadDemo
static long result = -1;
public static void main(String[] args) throws InterruptedException
MyThread thread = new MyThread();
thread.start();
long r;
// 等待子线程算完
synchronized (ThreadDemo.class)
ThreadDemo.class.wait();
r = result;
System.out.println(r); // 100
leetcode1114 按序打印
保证操作都加锁并且加的是同一把锁
public class Foo
private int step = 1;
public Foo()
public void first(Runnable printFirst) throws InterruptedException
// printFirst.run() outputs "first". Do not change or remove this line.
while(true)
synchronized (this)
if (step == 1)
break;
Thread.yield();
wait();
printFirst.run();
synchronized (this)
step = 2;
public void second(Runnable printSecond) throws InterruptedException
// printSecond.run() outputs "second". Do not change or remove this line.
while(true)
synchronized (this)
if (step == 2)
break;
Thread.yield();
printSecond.run();
synchronized (this)
step = 3;
public void third(Runnable printThird) throws InterruptedException
// printThird.run() outputs "third". Do not change or remove this line.
while(true)
synchronized (this)
if (step == 3)
break;
Thread.yield();
printThird.run();
synchronized (this)
step = 4;
以上是关于多线程的通知机制的主要内容,如果未能解决你的问题,请参考以下文章
Day826.Java多线程等待&通知机制 -Java 并发编程实战