Java多线程中的条件对象Condition是在java.util.concurrent.locks包下的,它和synchronized关键字一样,可以协调线程的执行顺序和通信,不过其功能更为强大,可用于等待条件、通知单个线程和通知所有等待线程。
一、条件对象Condition的基本用法
1. 创建Condition对象
在使用Condition对象前,需要首先创建其关联的Lock对象。
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
2. await方法
由于Condition对象是和Lock对象关联的,所以在执行condition.await()方法之前,必须先获取锁。然后,await()方法将会释放该锁并进入等待状态,等待其他线程调用相应的条件通知方法将其唤醒。
lock.lock(); // 获取锁
try {
while(条件不满足) {
condition.await(); // 释放锁并等待条件满足
}
// 条件满足,继续执行其他操作
}
catch (InterruptedException e) {
e.printStackTrace();
}
finally {
lock.unlock(); // 释放锁
}
3. signal方法和signalAll方法
当某些条件满足时,线程应该通知其他正在等待该条件的线程,这时候,可以使用signal()或signalAll()方法。
- signal()方法:随机选择一条等待该条件的线程进行通知,一般情况下不推荐使用。
- signalAll()方法:通知所有等待该条件的线程。
lock.lock(); // 获取锁
try {
// 等待条件满足
while(条件不满足) {
condition.await();
}
// 条件满足,通知其他线程
condition.signalAll();
}
catch (InterruptedException e) {
e.printStackTrace();
}
finally {
lock.unlock(); // 释放锁
}
二、示例1:生产者和消费者问题
考虑一个经典的生产者和消费者问题,在此过程中,生产者和消费者同时运行,但在不同的时间执行。生产者生成产品并将其放入队列中,而消费者则从队列中取出产品并进行消费。
Java中的阻塞队列是典型的实现方式,而Condition则可以通过控制队列中元素的数量实现阻塞和解锁。
import java.util.LinkedList;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ProducerConsumerDemo {
final LinkedList<Integer> queue = new LinkedList<>();
final int capacity = 10;
final Lock lock = new ReentrantLock();
final Condition notEmpty = lock.newCondition();
final Condition notFull = lock.newCondition();
public void produce(int num){
lock.lock();
try{
while(queue.size() == capacity){
// 队列已满,等待队列释放空位
notFull.await();
}
for(int i = 0; i < num; ++i){
queue.add(i);
}
System.out.println("生产 " + num + " 个产品,当前队列长度为 " + queue.size());
// 队列非空,唤醒消费者
notEmpty.signalAll();
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void consume(int num){
lock.lock();
try{
while(queue.size() == 0){
// 队列为空,等待队列有新的产品
notEmpty.await();
}
for(int i = 0; i < num; ++i){
queue.remove();
}
System.out.println("消费 " + num + " 个产品,当前队列长度为 " + queue.size());
// 队列未满,唤醒生产者
notFull.signalAll();
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
在生产者和消费者的类中,创建了空的LinkedList队列,队列大小限制为10。当生产者启动时,它将连续生产num个项目,并将它们添加到队列中。队列使用锁对同步化访问进行保护,并使用条件对象notEmpty和notFull调用await和signalAll方法。
三、示例2:条件队列的测试和使用
假设我们希望封装多个线程对象作为一个辅助类对象。这个线程安全的队列最初创建为一(非常)小的列表,其阈值设为零,当它既满又空时,能够适当地阻塞既生产者又消费者。
import java.util.LinkedList;
import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionQueueDemo {
private LinkedList<Integer> storage;
private int maxSize = 10;
private Lock lock;
private Condition isFull;
private Condition isEmpty;
public ConditionQueueDemo(int size) {
maxSize = size;
storage = new LinkedList<>();
lock = new ReentrantLock();
isFull = lock.newCondition();
isEmpty = lock.newCondition();
}
public void put(int num) throws InterruptedException {
lock.lock();
try {
while (storage.size() == maxSize) { // 若队列已满
System.out.println(Thread.currentThread().getName()+ " 队列已满,等待...");
isFull.await(); // 阻塞生产者线程,等待非满信号
}
Random random = new Random();
Thread.sleep(random.nextInt(1000)); // 随机等待
storage.add(num); // 将数值num加入队列尾部
System.out.println("[" + Thread.currentThread().getName() + "] 生产者 => "
+ num + ", 当前队列长度为:[" + storage.size() + "]/" + maxSize);
isEmpty.signalAll(); // 通知所有的消费者线程
} finally {
lock.unlock();
}
}
public void get() throws InterruptedException {
lock.lock();
try {
while (storage.size() == 0) { // 若队列已空
System.out.println(Thread.currentThread().getName() + " 队列已空,等待...");
isEmpty.await(); // 阻塞消费者线程,等待非空信号
}
Random random = new Random();
Thread.sleep(random.nextInt(1000)); // 随机等待
int val = storage.poll(); // 从队列头部弹出一个数值
System.out.println("[" + Thread.currentThread().getName() + "] 消费者 => "
+ val + ", 当前队列长度为:[" + storage.size() + "]/" + maxSize);
isFull.signalAll(); // 通知所有的生产者线程
} finally {
lock.unlock();
}
}
public int size() {
return storage.size();
}
public static void main(String[] args) {
ConditionQueueDemo queue = new ConditionQueueDemo(5);
for (int i = 1; i <= 10; i++) {
new Thread(() -> {
try {
while (true) {
queue.put(new Random().nextInt(100)); // 随机数加入队列
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "Producer").start();
}
for (int i = 1; i <= 10; i++) {
new Thread(() -> {
try {
while (true) {
queue.get(); // 从队列中获取数值
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "Consumer").start();
}
}
}
在示例中,我们通过随机生成的数值执行生产和消费任务,在队列已满或队列已空的情况下,线程通过调用await()方法阻塞并等待队列空或队列非满状态。在这两种情况下,线程被阻塞,等待相应程度的唤醒。如果在相应时间内访问了空队列或非空队列,则队列中的线程将被唤醒并继续运行。
以上就是Java多线程之条件对象Condition的完整攻略。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java多线程之条件对象Condition - Python技术站