调用wait方法,应当用循环之内调用 , 永远不要在循环外调用

正经学徒,佛系记录,不搞事情

直接明了,wait在百分99的情况下都是跟while联用

 以如下代码为例:

public class Container<T> {
    final private LinkedList<T> lists = new LinkedList<>();
    final private int MAX = 10;
    public synchronized void put(T t){
        while(lists.size() == MAX){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        lists.add(t);
        this.notifyAll();
    }

    public synchronized T get(){
        while(lists.size() == 0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        T t = lists.removeFirst();
        this.notifyAll();
        return t;
    }

    public static void main(String[] args) {
        Container<String> c = new Container<>();
        //启动5个消费线程
        for(int i=0;i<5;i++){
            new Thread(()->{
                for (int j = 0; j < 5; j++) {
                    System.out.println(c.get());
                }
            },"c"+i).start();
        }

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //启动2个生产者线程
        for(int i=0;i<2;i++){
            new Thread(()->{
                for (int j = 0; j < 10; j++) {
                   c.put(Thread.currentThread().getName()+" "+j);
                }
            },"p"+i).start();
        }
    }
}

模拟一个同步容器,提供put方法插入数据,容器最多只能存放10个;提供get方法,容器必须存在数据时才能获取;并启动5个消费者线程和2个生产者线程,疑问就出在为什么要用while循环里面使用wait而不直接用if一次性判断

解释一下具体的流程:

假设当前容器已经满了10个, 线程A在调用get方法,线程B 线程C 在调用put方法,正常情况是线程B C被阻塞,需要等待线程A调用get方法后让容器减少一个才能继续put

  • 假设 线程B 先调用了put方法,到达this.wait方法后进入等待并释放锁;
  • 线程C 调用了put方法,也到达this.wait方法后进入等待并释放锁;
  • 此时 线程A 调用get方法拿走一条数据后this.notifyAll();唤醒 线程B C
  • 如果wait使用的是if判断,则线程B C只会有一个正确 执行,假设线程A释放锁后,线程B拿到了锁,则线程B直接执行lists.add(t),方法执行完后释放锁,线程C再拿到锁,因为是if判断,所以线程C不会再继续判断容器是否已满,也直接往下执行lists.add(t)线程B C都会插入数据成功,使得当前容器容量超过最大限制;
  • 如果使用的是while线程B C被唤醒时,假设线程B获取到锁,顺利执行完方法插入了数据使当前容器又达到10个,线程C再拿到锁时,因为while循环,导致线程C在循环判断时因为容器已满,会再次调用this.wait方法等待并释放锁,以此保证了正确性

猜你喜欢

转载自blog.csdn.net/qq_31748587/article/details/102869509