一、概述
9.3 应用场景问题
Q6: 在什么情况下选择Semaphore而不是其他锁?
答案: Semaphore适用于以下场景:
- 资源池管理:如数据库连接池、线程池
- 限流控制:限制同时访问某个资源的线程数量
- 生产者消费者:控制缓冲区的容量
- 并发度控制:精确控制并发访问的数量
// 示例:限制同时下载的线程数
public class DownloadManager {
private final Semaphore downloadSemaphore = new Semaphore(3); // 最多3个并发下载
public void download(String url) {
try {
downloadSemaphore.acquire();
// 执行下载操作
performDownload(url);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
downloadSemaphore.release();
}
}
}
Q7: CountDownLatch和CyclicBarrier的区别?
答案:
| 特性 | CountDownLatch | CyclicBarrier |
|---|---|---|
| 复用性 | 一次性使用 | 可以重复使用 |
| 等待方式 | 一个或多个线程等待其他线程完成 | 所有线程相互等待 |
| 计数方向 | 递减到0 | 递增到指定值 |
| 触发条件 | 计数为0时释放等待线程 | 所有线程到达屏障点 |
| 使用场景 | 主线程等待子线程完成 | 多线程同步执行 |
9.4 性能优化问题
Q8: 如何减少锁竞争?
答案:
- 减少锁的粒度:使用分段锁、细粒度锁
- 减少锁的持有时间:将耗时操作移到锁外
- 使用读写锁:读多写少场景使用ReadWriteLock
- 使用无锁数据结构:如ConcurrentHashMap、AtomicInteger
- 锁分离:将读锁和写锁分离
- 避免锁嵌套:减少死锁风险
Q9: 什么是锁消除和锁粗化?
答案:
锁消除(Lock Elimination): JVM在编译时或运行时发现某些锁操作不可能存在竞争,会自动消除这些锁。
// JVM可能会消除这个锁,因为sb是局部变量
public String concatString(String s1, String s2) {
StringBuffer sb = new StringBuffer();
sb.append(s1);
sb.append(s2);
return sb.toString();
}
锁粗化(Lock Coarsening): JVM将多个连续的锁操作合并为一个更大范围的锁操作。
// 原始代码:多次加锁
for (int i = 0; i < 1000; i++) {
synchronized(obj) {
// 操作
}
}
// 优化后:锁粗化
synchronized(obj) {
for (int i = 0; i < 1000; i++) {
// 操作
}
}
9.5 实战问题
Q10: 如何实现一个支持超时的共享锁?
答案:
public class TimeoutSharedLock {
private final Semaphore semaphore;
public TimeoutSharedLock(int permits) {
this.semaphore = new Semaphore(permits);
}
public boolean tryAcquire(long timeout, TimeUnit unit) {
try {
return semaphore.tryAcquire(timeout, unit);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
public void release() {
semaphore.release();
}
public int availablePermits() {
return semaphore.availablePermits();
}
}
Q11: 如何避免读写锁的写饥饿问题?
答案: 写饥饿问题是指在读操作频繁的情况下,写操作可能长时间无法获得锁。解决方案:
- 使用公平锁:
ReadWriteLock fairLock = new ReentrantReadWriteLock(true);
- 限制读锁的获取:
public class WriterPreferenceReadWriteLock {
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private final AtomicInteger waitingWriters = new AtomicInteger(0);
public void readLock() {
// 如果有等待的写线程,读线程等待
while (waitingWriters.get() > 0) {
Thread.yield();
}
rwLock.readLock().lock();
}
public void writeLock() {
waitingWriters.incrementAndGet();
try {
rwLock.writeLock().lock();
} finally {
waitingWriters.decrementAndGet();
}
}
}
- 使用StampedLock:
public class StampedLockExample {
private final StampedLock sl = new StampedLock();
private double x, y;
void write(double newX, double newY) {
long stamp = sl.writeLock();
try {
x = newX;
y = newY;
} finally {
sl.unlockWrite(stamp);
}
}
double distanceFromOrigin() {
long stamp = sl.tryOptimisticRead();
double curX = x, curY = y;
if (!sl.validate(stamp)) {
stamp = sl.readLock();
try {
curX = x;
curY = y;
} finally {
sl.unlockRead(stamp);
}
}
return Math.sqrt(curX * curX + curY * curY);
}
}
总结
核心要点回顾
-
独占锁特点:
- 同时只允许一个线程持有
- 适用于写操作和数据修改
- 安全性高,并发度低
- 典型实现:synchronized、ReentrantLock
-
共享锁特点:
- 允许多个线程同时持有
- 适用于读操作和数据查询
- 并发度高,读性能好
- 典型实现:ReadWriteLock、Semaphore
-
选择原则:
- 读多写少:选择读写锁
- 需要超时/中断:选择ReentrantLock
- 简单互斥:选择synchronized
- 控制并发数:选择Semaphore
- 等待协调:选择CountDownLatch/CyclicBarrier
-
性能优化:
- 减少锁粒度和持有时间
- 使用合适的锁类型
- 避免锁竞争和死锁
- 考虑使用无锁数据结构
-
最佳实践:
- 始终在finally块中释放锁
- 避免在锁内进行耗时操作
- 按固定顺序获取多个锁
- 合理选择公平锁和非公平锁
发展趋势
随着Java并发编程的发展,出现了更多高性能的同步工具:
- StampedLock:提供乐观读锁,进一步提升读性能
- CompletableFuture:异步编程模型,减少线程阻塞
- Reactive Streams:响应式编程,处理高并发场景
- Virtual Threads:轻量级线程,降低线程创建成本
理解独占锁和共享锁的区别和应用场景,是掌握Java并发编程的重要基础。在实际开发中,应该根据具体的业务需求和性能要求,选择合适的同步机制,并遵循最佳实践,编写高效、安全的并发代码。
1.1 基本定义
独占锁(Exclusive Lock):也称为排他锁、写锁,同一时刻只能有一个线程持有锁,其他线程必须等待。
共享锁(Shared Lock):也称为读锁,同一时刻可以有多个线程同时持有锁,但不能与独占锁同时存在。
1.2 锁的分类体系
锁的分类
├── 按访问性质分类
│ ├── 独占锁(排他锁)
│ │ ├── ReentrantLock
│ │ ├── synchronized
│ │ └── WriteLock
│ └── 共享锁
│ ├── ReadLock
│ ├── Semaphore
│ └── CountDownLatch
├── 按公平性分类
│ ├── 公平锁
│ └── 非公平锁
└── 按可重入性分类
├── 可重入锁
└── 不可重入锁
1.3 应用场景概览
| 锁类型 | 适用场景 | 典型实现 | 性能特点 |
|---|---|---|---|
| 独占锁 | 写操作、临界资源修改 | ReentrantLock、synchronized | 安全性高,并发度低 |
| 共享锁 | 读操作、数据查询 | ReadWriteLock.readLock() | 并发度高,读性能好 |
二、独占锁详解
2.1 独占锁的特点
/**
* 独占锁的核心特点:
* 1. 互斥性:同一时刻只有一个线程能持有锁
* 2. 排他性:持有锁的线程可以读写,其他线程被阻塞
* 3. 安全性:保证数据的一致性和完整性
*/
public class ExclusiveLockDemo {
private final ReentrantLock lock = new ReentrantLock();
private int count = 0;
public void increment() {
lock.lock();
try {
// 只有一个线程能执行这里的代码
count++;
System.out.println(Thread.currentThread().getName() +
" incremented count to: " + count);
} finally {
lock.unlock();
}
}
public int getCount() {
lock.lock();
try {
return count;
} finally {
lock.unlock();
}
}
}
2.2 synchronized关键字
/**
* synchronized是Java内置的独占锁
*/
public class SynchronizedDemo {
private int count = 0;
// 方法级别的synchronized
public synchronized void increment() {
count++;
}
// 代码块级别的synchronized
public void decrement() {
synchronized(this) {
count--;
}
}
// 静态方法的synchronized(类锁)
public static synchronized void staticMethod() {
// 锁定的是Class对象
}
}
2.3 ReentrantLock详解
/**
* ReentrantLock是可重入的独占锁
*/
public class ReentrantLockDemo {
private final ReentrantLock lock = new ReentrantLock();
public void method1() {
lock.lock();
try {
System.out.println("Method1 acquired lock");
method2(); // 可重入:同一线程可以再次获取锁
} finally {
lock.unlock();
}
}
public void method2() {
lock.lock();
try {
System.out.println("Method2 acquired lock");
// 执行业务逻辑
} finally {
lock.unlock();
}
}
// 尝试获取锁(非阻塞)
public boolean tryLockExample() {
if (lock.tryLock()) {
try {
// 获取锁成功,执行业务逻辑
return true;
} finally {
lock.unlock();
}
} else {
// 获取锁失败,执行其他逻辑
return false;
}
}
// 超时获取锁
public boolean tryLockWithTimeout() throws InterruptedException {
if (lock.tryLock(5, TimeUnit.SECONDS)) {
try {
// 在5秒内获取到锁
return true;
} finally {
lock.unlock();
}
} else {
// 5秒内未获取到锁
return false;
}
}
}
2.4 独占锁的AQS实现
/**
* 独占锁在AQS中的实现原理
*/
public class ExclusiveLockAQSAnalysis {
/*
* AQS中独占锁的核心方法:
*
* 1. tryAcquire(int arg):尝试获取锁
* - 返回true表示获取成功
* - 返回false表示获取失败,需要排队
*
* 2. tryRelease(int arg):尝试释放锁
* - 返回true表示完全释放
* - 返回false表示部分释放(重入锁场景)
*
* 3. isHeldExclusively():判断是否独占持有
*/
// ReentrantLock中的实现示例
static final class NonfairSync extends AbstractQueuedSynchronizer {
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 锁未被占用,尝试获取
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
// 重入逻辑
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
}
}
三、共享锁详解
3.1 共享锁的特点
/**
* 共享锁的核心特点:
* 1. 共享性:多个线程可以同时持有锁
* 2. 读优化:适合读多写少的场景
* 3. 条件限制:不能与独占锁同时存在
*/
public class SharedLockDemo {
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Lock readLock = rwLock.readLock();
private final Lock writeLock = rwLock.writeLock();
private String data = "initial data";
// 读操作:使用共享锁
public String readData() {
readLock.lock();
try {
System.out.println(Thread.currentThread().getName() + " reading: " + data);
// 模拟读操作耗时
Thread.sleep(1000);
return data;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return null;
} finally {
readLock.unlock();
}
}
// 写操作:使用独占锁
public void writeData(String newData) {
writeLock.lock();
try {
System.out.println(Thread.currentThread().getName() + " writing: " + newData);
this.data = newData;
// 模拟写操作耗时
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
writeLock.unlock();
}
}
}
3.2 Semaphore信号量
/**
* Semaphore是一种共享锁的实现
* 允许指定数量的线程同时访问资源
*/
public class SemaphoreDemo {
// 允许3个线程同时访问
private final Semaphore semaphore = new Semaphore(3);
public void accessResource() {
try {
// 获取许可
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + " acquired permit");
// 模拟资源访问
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
// 释放许可
semaphore.release();
System.out.println(Thread.currentThread().getName() + " released permit");
}
}
// 尝试获取多个许可
public boolean tryAcquireMultiple(int permits) {
try {
if (semaphore.tryAcquire(permits, 3, TimeUnit.SECONDS)) {
System.out.println("Acquired " + permits + " permits");
return true;
} else {
System.out.println("Failed to acquire " + permits + " permits");
return false;
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
}
3.3 CountDownLatch倒计时门闩
/**
* CountDownLatch是一种特殊的共享锁
* 允许一个或多个线程等待其他线程完成操作
*/
public class CountDownLatchDemo {
private final CountDownLatch startLatch = new CountDownLatch(1);
private final CountDownLatch endLatch = new CountDownLatch(3);
public void workerThread(int workerId) {
try {
// 等待开始信号
System.out.println("Worker " + workerId + " waiting for start signal");
startLatch.await();
// 执行工作
System.out.println("Worker " + workerId + " started working");
Thread.sleep(2000 + workerId * 1000); // 模拟不同的工作时间
System.out.println("Worker " + workerId + " finished working");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
// 完成工作,计数减一
endLatch.countDown();
}
}
public void coordinatorThread() {
try {
System.out.println("Coordinator preparing...");
Thread.sleep(1000);
// 发出开始信号
System.out.println("Coordinator sending start signal");
startLatch.countDown();
// 等待所有工作完成
System.out.println("Coordinator waiting for all workers to finish");
endLatch.await();
System.out.println("All workers finished, coordinator proceeding");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
3.4 共享锁的AQS实现
/**
* 共享锁在AQS中的实现原理
*/
public class SharedLockAQSAnalysis {
/*
* AQS中共享锁的核心方法:
*
* 1. tryAcquireShared(int arg):尝试获取共享锁
* - 返回负数表示获取失败
* - 返回0表示获取成功但后续获取可能失败
* - 返回正数表示获取成功且后续获取也可能成功
*
* 2. tryReleaseShared(int arg):尝试释放共享锁
* - 返回true表示释放后可能有等待线程需要唤醒
* - 返回false表示释放后无需唤醒等待线程
*/
// Semaphore中的实现示例
static final class NonfairSync extends AbstractQueuedSynchronizer {
NonfairSync(int permits) {
setState(permits);
}
protected int tryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
protected final boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();
int next = current + releases;
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
if (compareAndSetState(current, next))
return true;
}
}
}
// CountDownLatch中的实现示例
static final class Sync extends AbstractQueuedSynchronizer {
Sync(int count) {
setState(count);
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
protected boolean tryReleaseShared(int releases) {
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
}
四、核心区别对比
4.1 功能特性对比
| 特性 | 独占锁 | 共享锁 |
|---|---|---|
| 并发访问 | 同时只允许一个线程 | 同时允许多个线程 |
| 读写操作 | 读写都互斥 | 读读不互斥,读写互斥 |
| 性能 | 并发度低,安全性高 | 并发度高,读性能好 |
| 适用场景 | 写操作、数据修改 | 读操作、数据查询 |
| 典型实现 | synchronized、ReentrantLock | ReadWriteLock、Semaphore |
| AQS方法 | tryAcquire/tryRelease | tryAcquireShared/tryReleaseShared |
4.2 性能对比示例
/**
* 独占锁 vs 共享锁性能对比
*/
public class LockPerformanceComparison {
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private final ReentrantLock exclusiveLock = new ReentrantLock();
private volatile String data = "test data";
// 使用独占锁的读操作
public String readWithExclusiveLock() {
exclusiveLock.lock();
try {
// 模拟读操作
Thread.sleep(10);
return data;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return null;
} finally {
exclusiveLock.unlock();
}
}
// 使用共享锁的读操作
public String readWithSharedLock() {
rwLock.readLock().lock();
try {
// 模拟读操作
Thread.sleep(10);
return data;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return null;
} finally {
rwLock.readLock().unlock();
}
}
// 性能测试
public static void performanceTest() {
LockPerformanceComparison test = new LockPerformanceComparison();
int threadCount = 10;
int iterations = 1000;
// 测试独占锁
long startTime = System.currentTimeMillis();
CountDownLatch latch1 = new CountDownLatch(threadCount);
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
for (int j = 0; j < iterations; j++) {
test.readWithExclusiveLock();
}
latch1.countDown();
}).start();
}
try {
latch1.await();
long exclusiveTime = System.currentTimeMillis() - startTime;
System.out.println("Exclusive lock time: " + exclusiveTime + "ms");
// 测试共享锁
startTime = System.currentTimeMillis();
CountDownLatch latch2 = new CountDownLatch(threadCount);
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
for (int j = 0; j < iterations; j++) {
test.readWithSharedLock();
}
latch2.countDown();
}).start();
}
latch2.await();
long sharedTime = System.currentTimeMillis() - startTime;
System.out.println("Shared lock time: " + sharedTime + "ms");
System.out.println("Performance improvement: " +
(double)exclusiveTime / sharedTime + "x");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
4.3 状态转换图
独占锁状态转换:
[未锁定] --acquire--> [已锁定] --release--> [未锁定]
↑ ↓
└-------- reentrant ----┘
共享锁状态转换:
[未锁定] --acquire--> [共享锁定] --acquire--> [多重共享锁定]
↑ ↓ ↓
└-------- release ------┴-------- release ----┘
读写锁状态转换:
[未锁定] ←→ [读锁定] ←→ [多重读锁定]
↕
[写锁定]
五、实现原理
5.1 AQS中的实现差异
/**
* AQS中独占锁和共享锁的实现差异
*/
public class AQSImplementationDifference {
// 独占锁的获取流程
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
// 共享锁的获取流程
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
// 独占锁的释放流程
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h); // 只唤醒一个后继节点
return true;
}
return false;
}
// 共享锁的释放流程
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared(); // 可能唤醒多个等待节点
return true;
}
return false;
}
}
5.2 等待队列中的节点类型
/**
* AQS等待队列中的节点类型
*/
static final class Node {
// 共享模式标记
static final Node SHARED = new Node();
// 独占模式标记
static final Node EXCLUSIVE = null;
// 下一个等待条件的节点,或者共享模式标记
Node nextWaiter;
// 判断是否为共享模式
final boolean isShared() {
return nextWaiter == SHARED;
}
}
5.3 唤醒机制的差异
/**
* 独占锁和共享锁的唤醒机制差异
*/
public class WakeupMechanismDifference {
// 独占锁:只唤醒一个后继节点
private void unparkSuccessor(Node node) {
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);
}
// 共享锁:可能唤醒多个等待节点
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue;
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue;
}
if (h == head)
break;
}
}
// 共享锁获取成功后的传播机制
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head;
setHead(node);
// 如果还有剩余资源,继续唤醒后继节点
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
if (s == null || s.isShared())
doReleaseShared();
}
}
}
六、典型应用场景
6.1 独占锁应用场景
6.1.1 银行转账系统
/**
* 银行转账系统 - 独占锁保证数据一致性
*/
public class BankTransferSystem {
private final Map<String, Account> accounts = new ConcurrentHashMap<>();
private final ReentrantLock transferLock = new ReentrantLock();
public boolean transfer(String fromAccount, String toAccount, double amount) {
transferLock.lock();
try {
Account from = accounts.get(fromAccount);
Account to = accounts.get(toAccount);
if (from == null || to == null) {
return false;
}
if (from.getBalance() < amount) {
return false;
}
// 原子性操作:扣款和入账必须同时成功或失败
from.withdraw(amount);
to.deposit(amount);
System.out.println("Transfer successful: " + amount +
" from " + fromAccount + " to " + toAccount);
return true;
} finally {
transferLock.unlock();
}
}
static class Account {
private double balance;
private final ReentrantLock lock = new ReentrantLock();
public void withdraw(double amount) {
lock.lock();
try {
balance -= amount;
} finally {
lock.unlock();
}
}
public void deposit(double amount) {
lock.lock();
try {
balance += amount;
} finally {
lock.unlock();
}
}
public double getBalance() {
lock.lock();
try {
return balance;
} finally {
lock.unlock();
}
}
}
}
6.1.2 单例模式
/**
* 线程安全的单例模式 - 使用独占锁
*/
public class ThreadSafeSingleton {
private static volatile ThreadSafeSingleton instance;
private static final ReentrantLock lock = new ReentrantLock();
private ThreadSafeSingleton() {
// 私有构造函数
}
public static ThreadSafeSingleton getInstance() {
if (instance == null) {
lock.lock();
try {
// 双重检查锁定
if (instance == null) {
instance = new ThreadSafeSingleton();
}
} finally {
lock.unlock();
}
}
return instance;
}
}
6.2 共享锁应用场景
6.2.1 缓存系统
/**
* 缓存系统 - 读写锁优化读性能
*/
public class CacheSystem<K, V> {
private final Map<K, V> cache = new HashMap<>();
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Lock readLock = rwLock.readLock();
private final Lock writeLock = rwLock.writeLock();
// 读操作:使用共享锁,多个线程可以并发读取
public V get(K key) {
readLock.lock();
try {
V value = cache.get(key);
System.out.println(Thread.currentThread().getName() +
" reading key: " + key + ", value: " + value);
return value;
} finally {
readLock.unlock();
}
}
// 写操作:使用独占锁,保证数据一致性
public void put(K key, V value) {
writeLock.lock();
try {
cache.put(key, value);
System.out.println(Thread.currentThread().getName() +
" writing key: " + key + ", value: " + value);
} finally {
writeLock.unlock();
}
}
// 删除操作:使用独占锁
public V remove(K key) {
writeLock.lock();
try {
V removed = cache.remove(key);
System.out.println(Thread.currentThread().getName() +
" removed key: " + key);
return removed;
} finally {
writeLock.unlock();
}
}
// 获取缓存大小:使用共享锁
public int size() {
readLock.lock();
try {
return cache.size();
} finally {
readLock.unlock();
}
}
}
6.2.2 连接池管理
/**
* 数据库连接池 - 使用信号量控制并发访问
*/
public class DatabaseConnectionPool {
private final Queue<Connection> connections = new LinkedList<>();
private final Semaphore semaphore;
private final int maxConnections;
public DatabaseConnectionPool(int maxConnections) {
this.maxConnections = maxConnections;
this.semaphore = new Semaphore(maxConnections);
// 初始化连接池
for (int i = 0; i < maxConnections; i++) {
connections.offer(createConnection());
}
}
// 获取连接
public Connection getConnection() throws InterruptedException {
semaphore.acquire(); // 获取许可
synchronized (connections) {
return connections.poll();
}
}
// 释放连接
public void releaseConnection(Connection connection) {
if (connection != null) {
synchronized (connections) {
connections.offer(connection);
}
semaphore.release(); // 释放许可
}
}
// 尝试获取连接(非阻塞)
public Connection tryGetConnection() {
if (semaphore.tryAcquire()) {
synchronized (connections) {
return connections.poll();
}
}
return null;
}
// 获取可用连接数
public int getAvailableConnections() {
return semaphore.availablePermits();
}
private Connection createConnection() {
// 模拟创建数据库连接
return new MockConnection();
}
static class MockConnection implements Connection {
// 模拟连接实现
}
}
6.2.3 并行任务协调
/**
* 并行任务协调 - 使用CountDownLatch和CyclicBarrier
*/
public class ParallelTaskCoordinator {
// 使用CountDownLatch等待所有任务完成
public void executeTasksWithCountDownLatch() {
int taskCount = 5;
CountDownLatch latch = new CountDownLatch(taskCount);
for (int i = 0; i < taskCount; i++) {
final int taskId = i;
new Thread(() -> {
try {
System.out.println("Task " + taskId + " started");
Thread.sleep(1000 + taskId * 500); // 模拟任务执行
System.out.println("Task " + taskId + " completed");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
latch.countDown();
}
}).start();
}
try {
latch.await(); // 等待所有任务完成
System.out.println("All tasks completed!");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// 使用CyclicBarrier同步多个阶段
public void executePhaseBasedTasks() {
int workerCount = 3;
CyclicBarrier barrier = new CyclicBarrier(workerCount, () -> {
System.out.println("All workers reached barrier, proceeding to next phase");
});
for (int i = 0; i < workerCount; i++) {
final int workerId = i;
new Thread(() -> {
try {
for (int phase = 1; phase <= 3; phase++) {
System.out.println("Worker " + workerId + " executing phase " + phase);
Thread.sleep(1000 + workerId * 200); // 模拟工作
System.out.println("Worker " + workerId + " finished phase " + phase);
barrier.await(); // 等待其他工作线程
}
} catch (InterruptedException | BrokenBarrierException e) {
Thread.currentThread().interrupt();
}
}).start();
}
}
}
七、性能对比分析
7.1 读写比例对性能的影响
/**
* 不同读写比例下的性能测试
*/
public class ReadWriteRatioPerformanceTest {
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private final ReentrantLock exclusiveLock = new ReentrantLock();
private volatile int data = 0;
// 使用读写锁的操作
public int readWithRWLock() {
rwLock.readLock().lock();
try {
Thread.sleep(1); // 模拟读操作
return data;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return -1;
} finally {
rwLock.readLock().unlock();
}
}
public void writeWithRWLock(int value) {
rwLock.writeLock().lock();
try {
Thread.sleep(5); // 模拟写操作
data = value;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
rwLock.writeLock().unlock();
}
}
// 使用独占锁的操作
public int readWithExclusiveLock() {
exclusiveLock.lock();
try {
Thread.sleep(1);
return data;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return -1;
} finally {
exclusiveLock.unlock();
}
}
public void writeWithExclusiveLock(int value) {
exclusiveLock.lock();
try {
Thread.sleep(5);
data = value;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
exclusiveLock.unlock();
}
}
// 性能测试方法
public static void testPerformance(int readRatio, int writeRatio) {
ReadWriteRatioPerformanceTest test = new ReadWriteRatioPerformanceTest();
int totalOperations = 1000;
int threadCount = 10;
System.out.println("\nTesting with read:write ratio = " + readRatio + ":" + writeRatio);
// 测试读写锁
long startTime = System.currentTimeMillis();
CountDownLatch rwLatch = new CountDownLatch(threadCount);
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
Random random = new Random();
for (int j = 0; j < totalOperations; j++) {
if (random.nextInt(readRatio + writeRatio) < readRatio) {
test.readWithRWLock();
} else {
test.writeWithRWLock(j);
}
}
rwLatch.countDown();
}).start();
}
try {
rwLatch.await();
long rwTime = System.currentTimeMillis() - startTime;
System.out.println("ReadWriteLock time: " + rwTime + "ms");
// 测试独占锁
startTime = System.currentTimeMillis();
CountDownLatch exclusiveLatch = new CountDownLatch(threadCount);
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
Random random = new Random();
for (int j = 0; j < totalOperations; j++) {
if (random.nextInt(readRatio + writeRatio) < readRatio) {
test.readWithExclusiveLock();
} else {
test.writeWithExclusiveLock(j);
}
}
exclusiveLatch.countDown();
}).start();
}
exclusiveLatch.await();
long exclusiveTime = System.currentTimeMillis() - startTime;
System.out.println("Exclusive lock time: " + exclusiveTime + "ms");
System.out.println("Performance improvement: " +
String.format("%.2f", (double)exclusiveTime / rwTime) + "x");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
public static void main(String[] args) {
// 测试不同的读写比例
testPerformance(9, 1); // 90% 读,10% 写
testPerformance(7, 3); // 70% 读,30% 写
testPerformance(5, 5); // 50% 读,50% 写
testPerformance(3, 7); // 30% 读,70% 写
testPerformance(1, 9); // 10% 读,90% 写
}
}
7.2 锁竞争程度对性能的影响
/**
* 锁竞争程度对性能的影响测试
*/
public class LockContentionPerformanceTest {
public static void testLockContention() {
int[] threadCounts = {1, 2, 4, 8, 16, 32};
for (int threadCount : threadCounts) {
System.out.println("\nTesting with " + threadCount + " threads:");
// 测试独占锁
testExclusiveLock(threadCount);
// 测试共享锁(纯读操作)
testSharedLock(threadCount);
}
}
private static void testExclusiveLock(int threadCount) {
ReentrantLock lock = new ReentrantLock();
CountDownLatch latch = new CountDownLatch(threadCount);
AtomicLong totalTime = new AtomicLong(0);
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
long startTime = System.nanoTime();
for (int j = 0; j < 10000; j++) {
lock.lock();
try {
// 模拟临界区操作
Thread.yield();
} finally {
lock.unlock();
}
}
totalTime.addAndGet(System.nanoTime() - startTime);
latch.countDown();
}).start();
}
try {
latch.await();
System.out.println("Exclusive lock average time per thread: " +
totalTime.get() / threadCount / 1_000_000 + "ms");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
private static void testSharedLock(int threadCount) {
ReadWriteLock rwLock = new ReentrantReadWriteLock();
CountDownLatch latch = new CountDownLatch(threadCount);
AtomicLong totalTime = new AtomicLong(0);
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
long startTime = System.nanoTime();
for (int j = 0; j < 10000; j++) {
rwLock.readLock().lock();
try {
// 模拟读操作
Thread.yield();
} finally {
rwLock.readLock().unlock();
}
}
totalTime.addAndGet(System.nanoTime() - startTime);
latch.countDown();
}).start();
}
try {
latch.await();
System.out.println("Shared lock average time per thread: " +
totalTime.get() / threadCount / 1_000_000 + "ms");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
八、最佳实践
8.1 选择合适的锁类型
/**
* 锁选择的最佳实践
*/
public class LockSelectionBestPractices {
// 1. 读多写少场景:使用读写锁
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private Map<String, String> cache = new HashMap<>();
public String getFromCache(String key) {
rwLock.readLock().lock();
try {
return cache.get(key);
} finally {
rwLock.readLock().unlock();
}
}
public void putToCache(String key, String value) {
rwLock.writeLock().lock();
try {
cache.put(key, value);
} finally {
rwLock.writeLock().unlock();
}
}
// 2. 需要超时或中断响应:使用ReentrantLock
private final ReentrantLock timeoutLock = new ReentrantLock();
public boolean tryOperationWithTimeout() {
try {
if (timeoutLock.tryLock(5, TimeUnit.SECONDS)) {
try {
// 执行操作
return true;
} finally {
timeoutLock.unlock();
}
} else {
System.out.println("Failed to acquire lock within timeout");
return false;
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
// 3. 简单的互斥访问:使用synchronized
private int counter = 0;
public synchronized void incrementCounter() {
counter++;
}
// 4. 控制并发访问数量:使用Semaphore
private final Semaphore semaphore = new Semaphore(3);
public void accessLimitedResource() {
try {
semaphore.acquire();
try {
// 访问受限资源
System.out.println("Accessing limited resource");
Thread.sleep(1000);
} finally {
semaphore.release();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
8.2 避免常见陷阱
/**
* 锁使用的常见陷阱和解决方案
*/
public class LockPitfallsAndSolutions {
// 陷阱1:死锁
private final Object lock1 = new Object();
private final Object lock2 = new Object();
// 错误的做法:可能导致死锁
public void badMethod1() {
synchronized(lock1) {
synchronized(lock2) {
// 业务逻辑
}
}
}
public void badMethod2() {
synchronized(lock2) {
synchronized(lock1) {
// 业务逻辑
}
}
}
// 正确的做法:按固定顺序获取锁
public void goodMethod1() {
synchronized(lock1) {
synchronized(lock2) {
// 业务逻辑
}
}
}
public void goodMethod2() {
synchronized(lock1) {
synchronized(lock2) {
// 业务逻辑
}
}
}
// 陷阱2:忘记释放锁
private final ReentrantLock lock = new ReentrantLock();
// 错误的做法:可能忘记释放锁
public void badUnlockMethod() {
lock.lock();
if (someCondition()) {
return; // 忘记释放锁!
}
lock.unlock();
}
// 正确的做法:使用try-finally
public void goodUnlockMethod() {
lock.lock();
try {
if (someCondition()) {
return;
}
// 其他业务逻辑
} finally {
lock.unlock();
}
}
// 陷阱3:读写锁的锁升级
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private String data = "initial";
// 错误的做法:尝试锁升级(不支持)
public void badLockUpgrade() {
rwLock.readLock().lock();
try {
if (needsUpdate(data)) {
// 这里会死锁!ReentrantReadWriteLock不支持锁升级
rwLock.writeLock().lock();
try {
data = updateData(data);
} finally {
rwLock.writeLock().unlock();
}
}
} finally {
rwLock.readLock().unlock();
}
}
// 正确的做法:先释放读锁,再获取写锁
public void goodLockUpgrade() {
rwLock.readLock().lock();
boolean needsUpdate;
try {
needsUpdate = needsUpdate(data);
} finally {
rwLock.readLock().unlock();
}
if (needsUpdate) {
rwLock.writeLock().lock();
try {
// 重新检查条件(双重检查)
if (needsUpdate(data)) {
data = updateData(data);
}
} finally {
rwLock.writeLock().unlock();
}
}
}
private boolean someCondition() { return true; }
private boolean needsUpdate(String data) { return true; }
private String updateData(String data) { return data + "_updated"; }
}
8.3 性能优化建议
/**
* 锁性能优化的最佳实践
*/
public class LockPerformanceOptimization {
// 1. 减少锁的粒度
private final Object[] locks = new Object[16];
private final Map<String, String> data = new ConcurrentHashMap<>();
{
for (int i = 0; i < locks.length; i++) {
locks[i] = new Object();
}
}
public void finegrainedLocking(String key, String value) {
int lockIndex = Math.abs(key.hashCode()) % locks.length;
synchronized(locks[lockIndex]) {
data.put(key, value);
}
}
// 2. 减少锁的持有时间
private final ReentrantLock lock = new ReentrantLock();
private List<String> items = new ArrayList<>();
// 错误的做法:在锁内进行耗时操作
public void badLongHoldTime(String item) {
lock.lock();
try {
items.add(item);
// 耗时的网络调用
callRemoteService(item);
} finally {
lock.unlock();
}
}
// 正确的做法:缩短锁持有时间
public void goodShortHoldTime(String item) {
lock.lock();
try {
items.add(item);
} finally {
lock.unlock();
}
// 在锁外进行耗时操作
callRemoteService(item);
}
// 3. 使用读写锁优化读操作
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Map<String, Object> cache = new HashMap<>();
public Object optimizedRead(String key) {
rwLock.readLock().lock();
try {
return cache.get(key);
} finally {
rwLock.readLock().unlock();
}
}
// 4. 使用无锁数据结构
private final AtomicInteger atomicCounter = new AtomicInteger(0);
private final ConcurrentHashMap<String, String> concurrentMap = new ConcurrentHashMap<>();
public void lockFreeOperations() {
// 原子操作,无需加锁
atomicCounter.incrementAndGet();
// 线程安全的Map操作
concurrentMap.put("key", "value");
}
private void callRemoteService(String item) {
// 模拟远程服务调用
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
九、面试常见问题
9.1 核心概念问题
Q1: 独占锁和共享锁的主要区别是什么?
答案:
| 维度 | 独占锁 | 共享锁 |
|---|---|---|
| 并发性 | 同时只允许一个线程持有 | 允许多个线程同时持有 |
| 互斥性 | 完全互斥,读写都排斥 | 读读不互斥,读写互斥 |
| 适用场景 | 写操作、数据修改 | 读操作、数据查询 |
| 性能特点 | 安全性高,并发度低 | 并发度高,读性能好 |
| 典型实现 | synchronized、ReentrantLock | ReadWriteLock、Semaphore |
Q2: 什么时候应该使用读写锁?
答案: 读写锁适用于以下场景:
- 读多写少:读操作频率远高于写操作
- 读操作耗时:读操作本身比较耗时
- 数据一致性要求:需要保证读写的数据一致性
- 性能敏感:对读操作的性能有较高要求
不适用场景:
- 写操作频繁的场景
- 读操作很快完成的场景
- 简单的计数器等场景
Q3: ReentrantReadWriteLock支持锁升级吗?
答案: ReentrantReadWriteLock 不支持锁升级(从读锁升级到写锁),但支持锁降级(从写锁降级到读锁)。
// 锁降级示例(支持)
writeLock.lock();
try {
// 写操作
readLock.lock(); // 获取读锁
} finally {
writeLock.unlock(); // 释放写锁
}
try {
// 读操作
} finally {
readLock.unlock(); // 释放读锁
}
// 锁升级(不支持,会死锁)
readLock.lock();
try {
writeLock.lock(); // 这里会死锁!
// ...
} finally {
readLock.unlock();
}
9.2 实现原理问题
Q4: AQS如何区分独占锁和共享锁?
答案: AQS通过以下方式区分:
-
节点类型标记:
static final Node SHARED = new Node(); // 共享模式 static final Node EXCLUSIVE = null; // 独占模式 -
不同的模板方法:
- 独占锁:
tryAcquire()、tryRelease() - 共享锁:
tryAcquireShared()、tryReleaseShared()
- 独占锁:
-
不同的唤醒机制:
- 独占锁:只唤醒一个后继节点
- 共享锁:可能唤醒多个等待节点
Q5: 为什么共享锁需要传播机制?
答案: 共享锁的传播机制确保:
- 资源充足时:一个线程获取共享锁成功后,如果还有剩余资源,应该继续唤醒后续等待的共享锁线程
- 避免饥饿:防止共享锁线程长时间等待
- 提高并发度:最大化利用共享资源
文章标签
冬眠
博主专注于技术、阅读与思考。在这里记录学习、思考与生活。
