场景:
1 双筷子,1 个或者多个人,餐桌每次只能一个人上去吃饭,其他人等待餐桌上的人吃完才有机会上去吃 。餐桌上吃完的那个人必须通知在等待吃饭的人说自己吃完了,你们可以吃了,此时在等待的人收到这个信号后,通过争抢的方式看谁先抢到筷子谁就能上餐桌吃饭,其他人继续等待 ;
在并发机制中,相当于线程的并发问题,该问题的关键在于怎么实现 等待与通知 的调度机制 ?
怎么办:
最简单的方式就是让等待者不断地循环检查变量是否符合预期,如下面代码所示,在while循环中设置不满足的条件,如果条件满足就退出循环,从而完成等待者的工作。
while (<判断条件==true>) {
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
}
doSomething();
该段代码在不满足条件时就睡眠一段时间,其目的是减少过多的无效尝试,降低对处理器资源的浪费,上述方式存在以下问题:
1、难以确保及时性,在睡眠时,基本不消耗处理器资源,但是如果睡眠过久,就不能及时发现条件的变化,也就是及时性难以保证。
2、难以降低开销,如果睡眠时间降低为1毫秒,这样当前线程就能很迅速地发现条件的变化,但是却可能消耗更多的处理器资源,造成了无端的浪费。
以上两个问题,看似矛盾难以调和,但是通过Java的wait()/notify()实现的等待/通知机制就能够很好地解决这个矛盾并实现所需的功能。
等待/通知的相关方法是任意Java对象都具备的!!!因为这些方法被定义在所有对象的超类java.lang.Object上,Object作为java中所有对象的基类,其存在的价值不言而喻,其中wait()和notify()方法的实现为多线程协作提供了保证。
等待/通知的相关方法描述如下:
也可以看官方的JDK-API文档:https://docs.oracle.com/javase/8/docs/api/index.html
方法名称 | 描述 |
---|---|
notify() | 通知一个在对象上等待的线程,使其从wait()返回,而返回的前提是该线程获取到了对象的锁。 |
notifyAll() | 通知所有等待在该对象上的线程。 |
wait() | 通调用该方法的线程进入WAITING状态,只有等待另外线程的通知或被中断才会返回,需要注意,调用wait()方法后,会释放对象的锁 。 |
wait(long) | 超时等待一段时间,这里的参数是毫秒,也就是等待长达n毫秒,如果没有通知就超时返回。 |
wait(long, int) | 对于超时时间更细粒度的控制,可以达到毫秒。 |
关于wait() / notify()机制先看下网上的一个demo:
public class WaitNotifyTest {
public static void main(String[] args) {
Object lock = new Object();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程A等待获取lock锁");
synchronized (lock) {
try {
System.out.println("线程A获取了lock锁");
Thread.sleep(1000);
System.out.println("线程A将要运行lock.wait()方法进行等待");
lock.wait();
System.out.println("线程A等待结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程B等待获取lock锁");
synchronized (lock) {
System.out.println("线程B获取了lock锁");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程B将要运行lock.notify()方法进行通知");
lock.notify();
}
}
}).start();
}
}
运行结果:
线程A等待获取lock锁
线程A获取了lock锁
线程B等待获取lock锁
线程A将要运行lock.wait()方法进行等待
线程B获取了lock锁
线程B将要运行lock.notify()方法进行通知
线程A等待结束
可以看到通过wait()/notify()完美实现了等待/通知机制 。
方法解析:
偷一下懒,贴个官方的wait() / notify() 方法详解上来,嘿嘿~
wait():
https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#wait-long-
notify():
https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#notify–
Mark:
- wait() / notify() 机制是多线程之间通信的一种方式 ;
- 线程之间获取锁没有先后顺序,而是通过随机、抢占的方式,可能导致线程饥饿 ;