关于wait()方法的使用
1、关于使用wait()方法为什么要加锁(否则会抛出 IllegalMonitorStateException):为了预防饥饿线程的产生(长期没有得到运行的线程)。
如下所示:
// 线程A 的代码
while(!condition){ // 不能使用 if , 因为存在一些特殊情况, 使得线程没有收到 notify 时也能退出等待状态
wait();
}
// do something
// 线程 B 的代码
if(!condition){
// do something ...
condition = true;
notify();
}
现在考虑, 如果wait() 和 notify() 的操作没有相应的同步机制, 则会发生如下情况
【线程A】 进入了 while 循环后(通过了 !condition 判断条件, 但尚未执行 wait 方法), CPU 时间片耗尽, CPU 开始执行线程B的代码
【线程B】 执行完毕了 condition = true; notify(); 的操作, 此时【线程A】的 wait() 操作尚未被执行, notify() 操作没有产生任何效果
【线程A】执行wait() 操作, 进入等待状态,如果没有额外的 notify() 操作, 该线程将持续在 condition = true 的情形下, 持续处于等待状态得不到执行。
2、为什么wait()一定要放在循环里??
多线程中,wait()的标准使用方法:
synchronized (monitor) {
// 判断条件谓词是否得到满足
while(!locked) {
// 等待唤醒
monitor.wait();
}
// 处理其他的业务逻辑
}
若是使用简单的判断,如下所示:
synchronized (monitor) {
// 判断条件谓词是否得到满足
if(!locked) {
// 等待唤醒
monitor.wait();
}
// 处理其他的业务逻辑
}
出现的问题:
睡眠的线程被唤醒之后有可能不满足while()中的条件判定,使用if不是很准确。详情参考下面的代码,当site发生变化的时候,km并没有发生变化。
/*
需求:
App有两个属性,site和km,当这两个属性其中一个发生变化的时候
需要进行输出,没有内容发生变化的时候就不发生变化
*/
public class App {
public String CITY = "shanghai";
public String site = "shanghai";
public int km=0;
public App() {
}
/*
地址发生变化。进行通知
*/
public synchronized void changeSite(String city) {
this.site = city;
notifyAll();
}
/*
里程数发生变化就进行通知
*/
public synchronized void changeKm() {
km = 101;
notifyAll();//只要有变化就进行通知
}
/*
一直进行等待地址的改变
*/
public synchronized void waitSite() {
while(CITY.equals(this.site)) {
try {
wait();
System.out.println(Thread.currentThread().getId()+" is waiting for site change~");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" has changed~");
}
/*
一直进行等待里程数的改变
*/
public synchronized void waitKm() {
while(this.km<100) {
try {
wait();
System.out.println(Thread.currentThread().getId()+" is smaller than 100 km");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" is bigger than 100 km");
}
/*
负责等待的线程
*/
class WaitSiteThread extends Thread {
@Override
public void run() {
waitSite();
}
}
class WaitKmThread extends Thread {
@Override
public void run() {
waitKm();
}
}
@Test
public void test() {
//启动三个进行等待的线程
for (int i = 0; i < 3; i++) {
new WaitSiteThread().start();
}
for (int i = 0; i < 3; i++) {
new WaitKmThread().start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
changeSite("beijing");
}
3、是谁在调用wait()和notify()??
被加锁的对象在调用wait()和notify()。
加锁的对象调用 wait() 方法后,线程进入等待状态,直到在加锁的对象上调用 notify() 或者 notifyAll() 方法来唤醒之前进入等待的线程
4、notify()之后会不会释放锁呢?
不会释放锁,一直到执行的方法完成之后才释放锁。
测试方法:
在notify()后面加上睡眠时间。到时间之后才会释放锁。
参考链接:https://blog.csdn.net/lengxiao1993/article/details/52296220