关于wait()方法的使用

2020-01-13  本文已影响0人  全麦土司

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

上一篇下一篇

猜你喜欢

热点阅读