深入理解 Condition 重入锁

2023-09-28 11:28:03 浏览数 (2)

架构师之路:深入理解 Condition 重入锁

前言

我们经常需要面对复杂的多线程并发控制问题。在这方面,重入锁(Reentrant Lock)是一个常用的工具,它允许线程在持有锁的情况下再次获取同一个锁,从而避免了死锁等问题。而本文将深入探讨重入锁的其中一种实现方式——Condition,以及如何在实际开发中巧妙地使用它来管理多线程并发。本文将逐步介绍Condition重入锁的搭配类,为您提供详细的代码示例,让您的多线程编程水平更上一层楼。

1. 重入锁与Condition简介

1.1 重入锁(Reentrant Lock)

重入锁是一种高级的线程同步工具,与传统的synchronized关键字相比,它提供了更灵活的线程控制能力。重入锁允许一个线程多次获取同一把锁,而不会导致死锁。这使得它在复杂的并发控制场景中非常有用。

1.2 Condition

Condition是重入锁的一部分,它用于管理线程的等待和唤醒。Condition允许线程在某个条件不满足时进入等待状态,当条件满足时,其他线程可以通知等待的线程继续执行。Condition提供了await()和signal()(或signalAll())等方法来实现线程的等待和唤醒操作。

2. 使用Condition进行线程等待和唤醒

2.1 await()方法

await()方法用于将当前线程置于等待状态,直到其他线程调用signal()或signalAll()方法来唤醒它。await()方法可以指定一个超时时间,如果超过指定时间仍未被唤醒,线程也会自动苏醒。

示例代码:

代码语言:java复制
// 创建一个重入锁
ReentrantLock lock = new ReentrantLock();
// 创建一个与锁关联的Condition
Condition condition = lock.newCondition();

try {
    // 获取锁
    lock.lock();
    
    // 在条件不满足的情况下等待
    condition.await();
    
    // 执行其他操作
} catch (InterruptedException e) {
    e.printStackTrace();
} finally {
    // 释放锁
    lock.unlock();
}

2.2 signal()和signalAll()方法

signal()方法用于唤醒一个等待在Condition上的线程,而signalAll()方法则会唤醒所有等待在Condition上的线程。这两个方法通常与await()方法一起使用,用于实现线程间的协作。

示例代码:

代码语言:java复制
// 创建一个重入锁
ReentrantLock lock = new ReentrantLock();
// 创建一个与锁关联的Condition
Condition condition = lock.newCondition();

try {
    // 获取锁
    lock.lock();
    
    // 唤醒等待的线程
    condition.signal();
    
    // 执行其他操作
} finally {
    // 释放锁
    lock.unlock();
}

3. Condition的应用场景

Condition适用于许多多线程并发的场景,特别是当线程需要等待某个条件满足时才能继续执行时。以下是一些常见的应用场景:

3.1 生产者-消费者模型

在生产者-消费者模型中,生产者线程生产数据,而消费者线程消费数据。当队列为空时,消费者线程需要等待,当队列满时,生产者线程需要等待。这时就可以使用两个Condition分别控制生产者和消费者线程的等待和唤醒。

3.2 线程池管理

当线程池中的线程数量达到上限时,新任务需要等待有空闲线程时才能执行。这时可以使用一个Condition来管理等待执行的任务。

3.3 控制任务执行顺序

有时候需要按照特定的顺序执行一系列任务,这时可以使用多个Condition来实现任务的等待和唤醒。

4. 实际示例演示

接下来,我们将通过一个实际的示例来演示如何使用Condition来管理多线程并发。假设我们有一个简单的任务队列,多个线程可以向队列中添加任务,同时多个线程可以从队列中取出任务并执行。我们希望实现以下功能:

  1. 当队列为空时,消费者线程等待任务。
  2. 当队列满时,生产者线程等待空闲位置。
  3. 生产者生产完任务后唤醒一个等待的消费者线程。
  4. 消费者消费完任务后唤醒一个等待的生产者线程。

示例代码如下:

代码语言:java复制
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class TaskQueue {
    private final Queue<String> queue = new LinkedList<>();
    private final int maxSize = 10;
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition notEmpty = lock.newCondition();
    private final Condition notFull = lock.newCondition();

    public void produce(String task) throws InterruptedException {
        lock.lock();
        try {
            while (queue.size() >= maxSize) {
               

 // 队列已满,等待消费者消费
                notFull.await();
            }
            queue.add(task);
            System.out.println("Produced: "   task);
            // 唤醒等待的消费者线程
            notEmpty.signal();
        } finally {
            lock.unlock();
        }
    }

    public String consume() throws InterruptedException {
        lock.lock();
        try {
            while (queue.isEmpty()) {
                // 队列为空,等待生产者生产
                notEmpty.await();
            }
            String task = queue.poll();
            System.out.println("Consumed: "   task);
            // 唤醒等待的生产者线程
            notFull.signal();
            return task;
        } finally {
            lock.unlock();
        }
    }
}

5. 总结与互动

在本文中,我们深入探讨了Condition重入锁的搭配类,介绍了重入锁与Condition的基本概念,以及如何使用它们来管理多线程并发。我们还通过一个实际示例演示了Condition的应用,展示了如何实现生产者-消费者模型。希望通过本文的学习,您对多线程编程中的并发控制有了更深入的理解。

如果您喜欢这篇博客,欢迎点赞、评论并与我们互动。如果您有任何问题或建议,也请随时提出,我们将竭诚为您解答。多线程编程是一个复杂而有趣的领域,希望本文能帮助您在这方面取得更大的进步。感谢您的阅读!

我正在参与2023腾讯技术创作特训营第二期有奖征文,瓜分万元奖池和键盘手表

0 人点赞