介绍
如果我们希望A线程可以控制B线程,同时B线程也可以控制A线程,那么Condition对象可以帮到我们。
业务场景
此时,我们有一个队列item,他处于多线性下面调用
- 我们希望当item为空时,取数据的线程就会停止并等待数据添加
- 当item从空的状态被添加了数据后,取数据的线程就能继续运行
- 当item 满了 的时候,我们希望添加数据的线程就会停止并等待数据被提取
- 当item由满了后第一次被取走数据,我们希望添加数据的线程就能继续运行
以上就是,取数据线程和添加数据线程,根据队列中数据的“空”、“满”状态,相互协调和配合的情景
代码
现在我们用代码来实现上面的业务场景
- 初始化各个锁和对象
private final int length = 10;
BlockingQueue items = new ArrayBlockingQueue(length);
private final ReentrantLock lock = new ReentrantLock(false); //注意,这里需要使用“不公平锁”
private final Condition notEmpty = lock.newCondition(); //不能空
private final Condition notFUll = lock.newCondition(); //不能满
- 向队列中放数据
private Runnable put() {
return new Runnable() {
@Override
public void run() {
try {
while (true) {
lock.lockInterruptibly();
Thread.sleep(100);
if (items.size() == length) {
notFUll.await();
}
items.add(1);
System.out.println("添加了一个数据,当前数据总量:" + items.size());
notEmpty.signal();
lock.unlock();
}
} catch (InterruptedException e) {
notFUll.signal();
e.printStackTrace();
} finally {
lock.unlock();
}
}
};
}
- 提取队列中的数据
private Runnable get() {
return new Runnable() {
@Override
public void run() {
try {
while (true) {
lock.lockInterruptibly();
Thread.sleep(500);
if (items.size() == 0) {
notEmpty.await();
}
items.take();
notFUll.signal();
System.out.println("提取了一个数据,当前数据总量:" + items.size());
lock.unlock();
}
} catch (InterruptedException e) {
notFUll.signal();
e.printStackTrace();
} finally {
lock.unlock();
}
}
};
}
- 主方法,测试用的
Thread putThread = new Thread(test.put(), " PutThread");
Thread getThread = new Thread(test.get(), " GetThread");
putThread.start();
getThread.start();
结论
- 最后结果截图如下,2个进程相互的争夺锁资源,但是当线程出现满或者空状态时,会停止对方的线程:
常用的方法
最后,我们看一下Condition类常用的方法
- await() : 使当前的线程等待,同时释放当前的锁,当其他线程中使用signal() 或者signalAll()方法时,线程会重新获得锁并继续执行。或者当线程被中断时,也能跳出等待
- awaitUninterruptibly():与await()方法基本相同,但是它并不会在等待过程中相应中断
- singal() : 用于唤醒一个在等待中的线程,相对的singalAll方法会唤醒所有等待的线程。