当时在用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行,无法保证顺序。









网友评论