首页
技术小册
AIGC
面试刷题
技术文章
MAGENTO
云计算
视频课程
源码下载
PDF书籍
「涨薪秘籍」
登录
注册
01 | 可见性、原子性和有序性问题:并发编程Bug的源头
02 | Java内存模型:看Java如何解决可见性和有序性问题
03 | 互斥锁(上):解决原子性问题
04 | 互斥锁(下):如何用一把锁保护多个资源?
05 | 一不小心就死锁了,怎么办?
06 | 用“等待-通知”机制优化循环等待
07 | 安全性、活跃性以及性能问题
08 | 管程:并发编程的万能钥匙
09 | Java线程(上):Java线程的生命周期
10 | Java线程(中):创建多少线程才是合适的?
11 | Java线程(下):为什么局部变量是线程安全的?
12 | 如何用面向对象思想写好并发程序?
13 | 理论基础模块热点问题答疑
14 | Lock和Condition(上):隐藏在并发包中的管程
15 | Lock和Condition(下):Dubbo如何用管程实现异步转同步?
16 | Semaphore:如何快速实现一个限流器?
17 | ReadWriteLock:如何快速实现一个完备的缓存?
18 | StampedLock:有没有比读写锁更快的锁?
19 | CountDownLatch和CyclicBarrier:如何让多线程步调一致?
20 | 并发容器:都有哪些“坑”需要我们填?
21 | 原子类:无锁工具类的典范
22 | Executor与线程池:如何创建正确的线程池?
23 | Future:如何用多线程实现最优的“烧水泡茶”程序?
24 | CompletableFuture:异步编程没那么难
25 | CompletionService:如何批量执行异步任务?
26 | Fork/Join:单机版的MapReduce
27 | 并发工具类模块热点问题答疑
28 | Immutability模式:如何利用不变性解决并发问题?
29 | Copy-on-Write模式:不是延时策略的COW
30 | 线程本地存储模式:没有共享,就没有伤害
31 | Guarded Suspension模式:等待唤醒机制的规范实现
32 | Balking模式:再谈线程安全的单例模式
33 | Thread-Per-Message模式:最简单实用的分工方法
34 | Worker Thread模式:如何避免重复创建线程?
35 | 两阶段终止模式:如何优雅地终止线程?
36 | 生产者-消费者模式:用流水线思想提高效率
37 | 设计模式模块热点问题答疑
38 | 案例分析(一):高性能限流器Guava RateLimiter
39 | 案例分析(二):高性能网络应用框架Netty
40 | 案例分析(三):高性能队列Disruptor
41 | 案例分析(四):高性能数据库连接池HiKariCP
42 | Actor模型:面向对象原生的并发模型
43 | 软件事务内存:借鉴数据库的并发经验
44 | 协程:更轻量级的线程
45 | CSP模型:Golang的主力队员
当前位置:
首页>>
技术小册>>
Java并发编程实战
小册名称:Java并发编程实战
### 14 | Lock和Condition(上):隐藏在并发包中的管程 在Java并发编程的广阔天地中,`Lock`与`Condition`是构建高性能、可靠并发应用的核心构件。它们不仅提供了比传统`synchronized`关键字和`wait()`/`notify()`/`notifyAll()`方法更为灵活和强大的锁机制,还隐含着管程(Monitor)的设计思想,为复杂并发控制提供了坚实的基础。本章将深入探讨`Lock`接口及其相关的`Condition`接口,揭示它们如何作为Java并发包中隐藏的管程实现,以及如何利用这些工具来解决实际并发编程中的挑战。 #### 1. 管程(Monitor)概述 在并发编程理论中,管程是一种用于管理共享资源的同步机制,它封装了共享数据以及对这些数据的操作,通过内部锁来保证同一时刻只有一个线程能够访问管程中的任意部分。管程的基本思想是将共享资源的访问权限封装在内部,任何线程想要访问这些资源,都必须通过管程提供的特定入口点进入,并在离开时释放资源。这种设计有效避免了并发冲突和数据不一致的问题。 Java的`synchronized`关键字和相关的`wait()`/`notify()`/`notifyAll()`方法,可以视为Java语言对管程概念的一种简化实现。然而,随着并发编程需求的复杂化,Java并发包(`java.util.concurrent`)提供了更为强大和灵活的`Lock`和`Condition`机制,以支持更细粒度的锁控制和更复杂的同步逻辑。 #### 2. Lock接口 `Lock`接口是Java并发包中提供的一个核心接口,它代表了一个基本的锁机制。与`synchronized`不同,`Lock`需要显式地获取和释放锁,这提供了更大的灵活性,同时也要求程序员必须更加谨慎地管理锁的生命周期,以避免死锁等问题。 ##### 2.1 主要方法 - `void lock()`: 获取锁。如果锁不可用,则当前线程将阻塞,直到锁可用。 - `void lockInterruptibly()`: 可中断地获取锁。如果当前线程在等待锁的过程中被中断,则当前线程会抛出`InterruptedException`,并释放已占有的所有锁。 - `boolean tryLock()`: 尝试获取锁,如果锁可用,则获取锁并返回`true`;如果锁不可用,则立即返回`false`,当前线程不会被阻塞。 - `boolean tryLock(long time, TimeUnit unit)`: 尝试在给定的时间内获取锁。如果锁在指定时间内变得可用,则返回`true`;如果锁在指定时间内始终不可用,则返回`false`。在等待过程中,当前线程可以被中断。 - `void unlock()`: 释放锁。在调用此方法之前,当前线程必须持有此锁。 ##### 2.2 优势与用途 - **灵活性**:`Lock`提供了比`synchronized`更多的灵活性,如尝试非阻塞地获取锁、可中断地获取锁等。 - **条件变量**:`Lock`可以绑定多个`Condition`对象,每个`Condition`对象都管理着一个等待/通知队列,这使得能够更精细地控制线程的等待/唤醒行为。 - **可重入性**:大多数`Lock`实现都是可重入的,即同一个线程可以多次获得同一个锁。 #### 3. Condition接口 `Condition`接口是`Lock`接口的一个补充,它提供了比`Object`监视器方法(`wait()`、`notify()`、`notifyAll()`)更强大的线程间通信能力。每个`Lock`对象都可以关联多个`Condition`对象,这允许线程以不同的条件等待和唤醒,从而实现更为复杂的同步控制逻辑。 ##### 3.1 主要方法 - `void await()`: 使当前线程等待直到另一个线程调用该条件的`signal()`或`signalAll()`方法。在调用此方法之前,当前线程必须持有相关的锁。 - `void awaitUninterruptibly()`: 与`await()`类似,但该方法对中断不敏感,即线程在等待过程中不会被中断。 - `long awaitNanos(long nanosTimeout)`: 使当前线程等待直到另一个线程调用该条件的`signal()`或`signalAll()`方法,或者直到等待时间超过指定的纳秒数。 - `boolean awaitUntil(Date deadline)`: 使当前线程等待直到另一个线程调用该条件的`signal()`或`signalAll()`方法,或者直到给定的截止时间到达。 - `void signal()`: 唤醒等待在该条件上的一个线程(如果存在)。在调用此方法之前,当前线程必须持有相关的锁。 - `void signalAll()`: 唤醒等待在该条件上的所有线程。在调用此方法之前,当前线程必须持有相关的锁。 ##### 3.2 相比`Object`监视器方法的优势 - **多个等待集合**:一个`Lock`可以有多个`Condition`实例,每个实例都管理着自己的等待队列,而`Object`监视器方法只有一个等待队列。 - **更灵活的等待/通知控制**:通过不同的`Condition`对象,可以基于不同的条件进行等待和唤醒,而`Object`监视器方法只能进行无差别的唤醒。 - **更精细的锁定控制**:由于`Condition`与`Lock`紧密关联,因此可以在不释放锁的情况下进行等待,从而避免了在`wait()`和`notify()`调用之间的重新获取锁的开销和潜在的竞争条件。 #### 4. 应用实例:生产者-消费者问题 生产者-消费者问题是并发编程中的一个经典问题,用于演示线程间如何安全地共享和交换数据。利用`Lock`和`Condition`,我们可以更灵活地解决这个问题。 ```java import java.util.LinkedList; import java.util.Queue; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class ProducerConsumerDemo { private final Queue<Integer> queue = new LinkedList<>(); private final int capacity; private final Lock lock = new ReentrantLock(); private final Condition notEmpty = lock.newCondition(); private final Condition notFull = lock.newCondition(); public ProducerConsumerDemo(int capacity) { this.capacity = capacity; } // 生产者方法 public void produce(int value) throws InterruptedException { lock.lock(); try { while (queue.size() == capacity) { notFull.await(); // 队列满时等待 } queue.add(value); System.out.println("Produced: " + value); notEmpty.signal(); // 通知消费者 } finally { lock.unlock(); } } // 消费者方法 public int consume() throws InterruptedException { lock.lock(); try { while (queue.isEmpty()) { notEmpty.await(); // 队列空时等待 } int value = queue.poll(); System.out.println("Consumed: " + value); notFull.signal(); // 通知生产者 return value; } finally { lock.unlock(); } } // 主函数,用于演示 public static void main(String[] args) { ProducerConsumerDemo demo = new ProducerConsumerDemo(10); // 启动生产者线程 new Thread(() -> { for (int i = 0; i < 20; i++) { try { demo.produce(i); Thread.sleep(100); // 模拟生产耗时 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }).start(); // 启动消费者线程 new Thread(() -> { for (int i = 0; i < 20; i++) { try { demo.consume(); Thread.sleep(200); // 模拟消费耗时 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }).start(); } } ``` 在上述示例中,我们使用了`ReentrantLock`作为锁实现,并通过它创建了两个`Condition`对象:`notEmpty`和`notFull`,分别用于控制生产者在队列满时等待、消费者在队列空时等待,以及通知对方继续执行。这种实现方式比使用单个`synchronized`块和`wait()`/`notify()`方法更为清晰和灵活。 #### 5. 总结 `Lock`和`Condition`作为Java并发包中隐藏的管程实现,为并发编程提供了强大而灵活的工具。通过显式地获取和释放锁,以及基于不同条件的等待/唤醒机制,它们能够支持更为复杂的同步控制逻辑。在实际应用中,合理利用`Lock`和`Condition`,可以显著提升并发程序的性能和可靠性。然而,也需要注意,过度的锁使用和不当的同步控制,可能会导致死锁、活锁等并发问题,因此在使用时务必谨慎。
上一篇:
13 | 理论基础模块热点问题答疑
下一篇:
15 | Lock和Condition(下):Dubbo如何用管程实现异步转同步?
该分类下的相关小册推荐:
Java必知必会-JDBC
Java语言基础8-Java多线程
Java语言基础9-常用API和常见算法
Java语言基础16-JDK8 新特性
Mybatis合辑4-Mybatis缓存机制
Java语言基础5-面向对象初级
Java语言基础15-单元测试和日志技术
深入拆解 Java 虚拟机
Java语言基础12-网络编程
Java面试指南
Java语言基础7-Java中的异常
SpringBoot零基础到实战