一、API介绍
- obj.wait() 让进入object监视器的线程到waitSet中等待(RUNNBALE -> TIMED_WAITING)
- obj.notify() 随机唤醒一个waitSet中阻塞的线程
- obj.notifyAll() 唤醒在waitSet中所有阻塞的线程
上面三个方法都是线程间协作的手段,都属于Object对象的方法。必须获得此对象的锁,才可以调用这几个方法。
static Object obj = new Object();
public static void main(String[] args){
Thread t2 = new Thread(() -> {
synchronized (obj){
log.info("{}","t1进入等待...");
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("t1被唤醒");
}
}, "t1");
Thread t1 = new Thread(() -> {
synchronized (obj){
log.info("{}","t2进入等待...");
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("t2被唤醒");
}
}, "t2");
t1.start();
t2.start();
try {
//主线程休眠2秒
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj){
log.info("{}","主线程随机唤醒一个线程");
obj.notify();
}
}
输出结果:
21:35:24.174 INFO [t1] com.dxf.线程的方法.Test9 - t1进入等待...
21:35:24.175 INFO [t2] com.dxf.线程的方法.Test9 - t2进入等待...
21:35:26.173 INFO [main] com.dxf.线程的方法.Test9 - 主线程随机唤醒一个线程
21:35:26.173 INFO [t1] com.dxf.线程的方法.Test9 - t1被唤醒
如果换成notifyAll():
21:36:46.677 INFO [t2] com.dxf.线程的方法.Test9 - t2进入等待...
21:36:46.678 INFO [t1] com.dxf.线程的方法.Test9 - t1进入等待...
21:36:48.677 INFO [main] com.dxf.线程的方法.Test9 - 主线程唤醒全部waitSet中的线程
21:36:48.677 INFO [t1] com.dxf.线程的方法.Test9 - t1被唤醒
21:36:48.677 INFO [t2] com.dxf.线程的方法.Test9 - t2被唤醒
二、wait()和sleep()的区别
- sleep()是Thread类中的方法,wait()是object类中的方法.
- sleep()可以在任意位置执行,wait()只能在synchronized中获取到对象锁执行.
- sleep()在synchronized中执行不会释放对象锁,wait()则会进入waitSet队列中等待同时释放锁.
三、wait()和notify()的正确使用姿势
大家看下面一段代码:
static final Object lock = new Object();
static boolean flag = false;
static boolean hasDinner = false;
/**
* 现在有两个线程
* t1线程:需要有烟才能干活
* t2线程:等待外卖,外卖到了开始吃饭
* @param args
*/
public static void main(String[] args) {
new Thread(() ->{
synchronized (lock){
while (!flag){
//如果flag为false,则进入等待(会释放锁),当为ture时才会往下走
try {
log.info("{}","没有烟...不能干活...");
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.info("开始干活");
}
},"t1").start();
new Thread(() ->{
synchronized (lock){
while (!hasDinner){
//如果flag为false,则进入等待(会释放锁),当为ture时才会往下走
try {
log.info("{}","等待外卖...");
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.info("外卖到了...开始吃饭...");
}
},"t2").start();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock){
hasDinner = true;
lock.notify();
}
}
当主线程将送外卖的变量改为ture,表示外卖到了,t2线程应该被唤醒。可如果使用了notify()方法,只能随机唤醒一个在waitSet中的线程,输出结果:
20:56:33.551 INFO [t1] com.dxf.线程的方法.Test10 - 没有烟...不能干活...
20:56:33.552 INFO [t2] com.dxf.线程的方法.Test10 - 等待外卖...
20:56:35.552 INFO [t1] com.dxf.线程的方法.Test10 - 没有烟...不能干活...
我们发现t1线程被唤醒了,并且不能继续往下执行,跟我们想的有偏差,如何解决?
使用notifyAll().唤醒全部等待的线程,满足条件的会继续执行,不满足的则继续进入等待.
21:01:35.830 INFO [t1] com.dxf.线程的方法.Test10 - 没有烟...不能干活...
21:01:35.832 INFO [t2] com.dxf.线程的方法.Test10 - 等待外卖...
21:01:37.830 INFO [t2] com.dxf.线程的方法.Test10 - 外卖到了...开始吃饭...
21:01:37.830 INFO [t1] com.dxf.线程的方法.Test10 - 没有烟...不能干活...
网友评论