多线程两个Condition也必须用while判断条件

2019-06-14  本文已影响0人  _Exception

当时在用ReentrantLock练习生产者消费者,然后获得了两个condition,之前知道多线程条件判断要用while,因为notify不能指定唤醒,这次两个condition不就可以实现指定唤醒吗?就不需要while了吧。于是就用if测试,代码如下。

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class TestTranditonalProductorConsumer {

    public static class Basket {
        // 容量
        private int capacity = 2;
        // 当前数量
        private volatile int number = 0;

        private ReentrantLock reentrantLock = new ReentrantLock();
        // 等待get
        private Condition notFull = reentrantLock.newCondition();
        // 等待put
        private Condition notEmpty = reentrantLock.newCondition();

        public Basket put() throws InterruptedException {

            try {
                reentrantLock.lock();
                System.out.println(Thread.currentThread().getName() + "put 线程进入,number为" + number);

                if (number == capacity){
                    System.out.println(Thread.currentThread().getName() + "put 线程开始等待,number为" + number);
                    notEmpty.await();
                    System.out.println(Thread.currentThread().getName() + "put 线程醒过来了,number为" + number);
                }
                number = number + 1;
                notFull.signal();
                System.out.println(Thread.currentThread().getName() + "唤醒get,number为" + number);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                reentrantLock.unlock();
            }
            return this;
        }

        public Basket get() throws InterruptedException {
            try {
                reentrantLock.lock();
                System.out.println(Thread.currentThread().getName() + "get 线程进入,number为" + number);
                if (number == 0){
                    System.out.println(Thread.currentThread().getName() + "get 线程开始等待,number为" + number);
                    notFull.await();
                    System.out.println(Thread.currentThread().getName() + "get 线程醒过来了,number为" + number);
                }
                number = number - 1;
                notEmpty.signal();
                System.out.println(Thread.currentThread().getName() + "唤醒put,number为" + number);

            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                reentrantLock.unlock();
            }

            return this;
        }
    }

    public static void main(String[] args) {

        final Basket basket = new Basket();

        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                try {
                    basket.put();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, "put线程[" + i + "]").start();
        }

        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                try {
                    basket.get();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, "get线程[" + i + "]").start();
        }
    }
}

日志如下:

put线程[0]put 线程进入,number为0                  
put线程[0]唤醒get,number为1                          --put线程0结束
put线程[4]put 线程进入,number为1
put线程[4]唤醒get,number为2                          --put线程4结束
put线程[1]put 线程进入,number为2
put线程[1]put 线程开始等待,number为2           --put线程1阻塞
put线程[2]put 线程进入,number为2
put线程[2]put 线程开始等待,number为2           --put线程2阻塞
put线程[3]put 线程进入,number为2
put线程[3]put 线程开始等待,number为2           --put线程3阻塞,累计阻塞3个put线程
get线程[0]get 线程进入,number为2
get线程[0]唤醒put,number为1                          --唤醒put 1个,get线程0结束
get线程[2]get 线程进入,number为1
get线程[2]唤醒put,number为0                          --唤醒put 2个,get线程2结束
get线程[3]get 线程进入,number为0
get线程[3]get 线程开始等待,number为0           --get线程3阻塞
put线程[1]put 线程醒过来了,number为0           --put线程1醒过来开始执行,还剩2个put阻塞
put线程[1]唤醒get,number为1                      --put线程1执行结束,并唤醒get线程3(因为只有一个get线程目前阻塞)
get线程[1]get 线程进入,number为1
get线程[1]唤醒put,number为0                    --get线程1结束,唤醒put,还剩1个put阻塞
put线程[2]put 线程醒过来了,number为0          --put线程2醒来执行,并唤醒get线程一个(这里没有可以唤醒的get线程)
put线程[2]唤醒get,number为1                    --put线程2结束
get线程[4]get 线程进入,number为1                    
get线程[4]唤醒put,number为0                    --get线程4结束,唤醒put线程1个
get线程[3]get 线程醒过来了,number为0    --,到这里就是重点了,因为还剩一个就绪的get和一个就绪的put。。。所以问题就在这里。
get线程[3]唤醒put,number为-1
put线程[3]put 线程醒过来了,number为-1
put线程[3]唤醒get,number为0

问题就是倒数4行,无法保证顺序。

上一篇 下一篇

猜你喜欢

热点阅读