线程间通信
在多线程编程当中,很多时候我们需要一个线程修改了某个值之后,而另外一个线程能够感知到变化从而进行响应的操作。比较笨的方法呢就是不断轮询判断,如果条件满足进行响应的操作,反之则等待若干时间后再次轮询判断。就如下代码所示:
while(条件判断){
Thread.sleep(5000);
}
doSomeThing();
上面这段伪代码看似能够实现所需的功能,但是却有着很严重的问题:
- 难以确保及时性。在休眠的时候,确实不会消耗处理器资源,但是通过睡眠机制,就不能及时的发现条件已经变化。
- 难以降低开销。如果降低睡眠的时间,比如1毫秒,这样确实能迅速的发现条件变化,但是却可能消耗更多的处理器资源,造成不必要的浪费。
以上两个问题,看似矛盾难以取舍。但是Java通过等待/通知机制就能很好的解决问题并实现所需的功能。
初识 wait & notify
等待/通知相关方法是任意Java对象都具备的,因为这些方法是被定义在java.lang.Object对象上的,方法描述如下
方法名称 | 描述 |
---|---|
notify() | 通知一个在对象上等待的线程,使其从wait()方法返回,返回的前提是该线程重新获取到了对象的锁 |
notifyAll() | 通知所有等待在改对象上的线程 |
wait() | 调用该方法的线程进入阻塞状态并且会释放对象的锁,只有等待其他线程通知或被终端才会返回, |
wait(long) | 等待指定一段时间,时间一到就返回 |
wait(long,int) | 等待指定一段时间,对于超时时间更加细粒度的控制,可以达到纳秒 |
示例代码:
public class WaitNotifyTest {
static boolean flag = true;
static Object lock = new Object();
public static void main(String[] args) throws Exception {
Thread waitThread = new Thread(new Runnable() {
@Override
public void run() {
// 加锁
synchronized (lock) {
// 当条件不满足时,继续wait,同时释放了lock的锁
while (flag) {
try {
System.out.println(Thread.currentThread().getName() + " wait 开始");
lock.wait();
System.out.println(Thread.currentThread().getName() + " wait 结束");
} catch (InterruptedException e) {
}
}
// 条件满足时,完成工作
System.out.println(Thread.currentThread().getName() + " 开始工作");
}
}
}, "WaitThread");
waitThread.start();
TimeUnit.SECONDS.sleep(1);
Thread notifyThread = new Thread(new Runnable() {
@Override
public void run() {
// 加锁,拥有lock的Monitor
synchronized (lock) {
// 获取lock的锁,然后进行通知,通知时不会释放lock的锁,
// 直到当前线程释放了lock后,WaitThread才能从wait方法中返回
System.out.println(Thread.currentThread().getName() + " notify 开始");
lock.notifyAll();
flag = false;
System.out.println(Thread.currentThread().getName() + " notify 完成 开始睡眠");
SleepUtils.second(5);
System.out.println(Thread.currentThread().getName() + "睡眠结束即将释放锁");
}
}
}, "NotifyThread");
notifyThread.start();
}
}
输出内容:
image
通过上面的示例代码,会发现使用wait/notfiy相关方法时需要注意一下细节:
- 使用wait(),notify(),notifyAll()时需要先获取到该对象的锁。
- 使用wait()方法会使线程进入阻塞状态,并且会释放锁。
- 调用notify(),notifyAll()方法后,被通知的线程依旧不会从wait返回,需要调用notify()方法的线程释放锁之后,并且等待的线程重新获取到锁之后才会返回。
- notify()方法是将等待队列中的一个等待线程从等待队列移到同步队列中,而notifyAll()方法是讲等待队列中的所有线程全部移到同步队列中
wait & sleep 的区别
从表面上来看,wait和sleep都能使当前线程进入阻塞状态,但两者还是有着本质上的区别:
- wait和sleep方法都可以使线程进入阻塞状态,并且均是可中断方法,被中断后都会收到中断异常。
- wait是Object方法,sleep是Thread独有的方法。
- wait需要在同步方法中调用,而sleep不需要。
- wait方法会释放锁,而sleep方法并不会释放锁。
网友评论