概述
Java并发包(java.util.concurrent,简称JUC)提供了丰富的锁机制,这些锁是构建高性能并发应用的基石。作为架构师,深入理解这些锁的实现原理、性能特征和适用场景,对于设计高并发系统至关重要。
锁的分类体系
1. 按锁的性质分类
1.1 悲观锁 vs 乐观锁
悲观锁:假设并发冲突一定会发生,因此在访问共享资源前先获取锁。
- 代表:synchronized、ReentrantLock
- 特点:线程安全,但可能导致线程阻塞
- 适用场景:写操作频繁的场景
乐观锁:假设并发冲突很少发生,只在更新时检查是否有冲突。
- 代表:AtomicInteger、StampedLock的乐观读
- 特点:无锁化,性能高,但可能需要重试
- 适用场景:读操作频繁的场景
1.2 公平锁 vs 非公平锁
公平锁:严格按照线程请求锁的顺序分配锁。
// 公平锁示例
ReentrantLock fairLock = new ReentrantLock(true);
非公平锁:允许插队,新来的线程可能比等待的线程先获得锁。
// 非公平锁示例(默认)
ReentrantLock unfairLock = new ReentrantLock(false);
1.3 可重入锁 vs 不可重入锁
可重入锁:同一线程可以多次获取同一把锁。
- synchronized和ReentrantLock都是可重入的
- 避免了死锁问题
不可重入锁:同一线程不能多次获取同一把锁。
- 实现简单,但容易造成死锁
2. 按锁的实现机制分类
2.1 基于AQS的锁
AbstractQueuedSynchronizer(AQS)是JUC锁的核心框架:
// AQS核心思想示例
public abstract class AbstractQueuedSynchronizer {
private volatile int state; // 同步状态
private transient volatile Node head; // 等待队列头节点
private transient volatile Node tail; // 等待队列尾节点
// 独占模式获取锁
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
// 独占模式释放锁
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
}
2.2 基于CAS的无锁实现
// AtomicInteger的CAS实现示例
public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
}
核心锁实现详解
1. ReentrantLock
1.1 基本特性
- 可重入性
- 支持公平/非公平模式
- 支持中断
- 支持超时获取锁
- 支持条件变量
1.2 实现原理
public class ReentrantLock implements Lock {
private final Sync sync;
// 非公平同步器
static final class NonfairSync extends Sync {
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
// 公平同步器
static final class FairSync extends Sync {
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
}
1.3 最佳实践
public class SafeCounter {
private final ReentrantLock lock = new ReentrantLock();
private int count = 0;
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock(); // 确保在finally中释放锁
}
}
public boolean tryIncrementWithTimeout(long timeout, TimeUnit unit)
throws InterruptedException {
if (lock.tryLock(timeout, unit)) {
try {
count++;
return true;
} finally {
lock.unlock();
}
}
return false;
}
}
2. ReadWriteLock
2.1 设计思想
- 读读不互斥
- 读写互斥
- 写写互斥
2.2 实现示例
public class CachedData {
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Lock readLock = rwLock.readLock();
private final Lock writeLock = rwLock.writeLock();
private Object data;
private boolean cacheValid;
public Object getData() {
readLock.lock();
try {
if (!cacheValid) {
// 读锁升级为写锁(需要先释放读锁)
readLock.unlock();
writeLock.lock();
try {
if (!cacheValid) {
data = loadDataFromDatabase();
cacheValid = true;
}
// 写锁降级为读锁
readLock.lock();
} finally {
writeLock.unlock();
}
}
return data;
} finally {
readLock.unlock();
}
}
public void updateData(Object newData) {
writeLock.lock();
try {
data = newData;
cacheValid = true;
} finally {
writeLock.unlock();
}
}
private Object loadDataFromDatabase() {
// 模拟数据库加载
return new Object();
}
}
3. StampedLock
3.1 特性
- JDK 8引入的高性能读写锁
- 支持乐观读
- 不支持重入
- 不支持条件变量
3.2 三种锁模式
public class Point {
private final StampedLock sl = new StampedLock();
private double x, y;
// 写锁
public void write(double newX, double newY) {
long stamp = sl.writeLock();
try {
x = newX;
y = newY;
} finally {
sl.unlockWrite(stamp);
}
}
// 悲观读锁
public double distanceFromOrigin() {
long stamp = sl.readLock();
try {
return Math.sqrt(x * x + y * y);
} finally {
sl.unlockRead(stamp);
}
}
// 乐观读锁
public double distanceFromOriginOptimistic() {
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);
}
// 锁升级示例
public void moveIfAtOrigin(double newX, double newY) {
long stamp = sl.readLock();
try {
while (x == 0.0 && y == 0.0) {
// 尝试将读锁升级为写锁
long ws = sl.tryConvertToWriteLock(stamp);
if (ws != 0L) {
stamp = ws;
x = newX;
y = newY;
break;
} else {
// 升级失败,释放读锁,获取写锁
sl.unlockRead(stamp);
stamp = sl.writeLock();
}
}
} finally {
sl.unlock(stamp);
}
}
}
4. Condition条件变量
4.1 基本概念
Condition提供了类似Object.wait()/notify()的功能,但更加灵活。
4.2 生产者-消费者模式实现
public class BoundedBuffer<T> {
private final ReentrantLock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
private final Object[] items;
private int putIndex, takeIndex, count;
public BoundedBuffer(int capacity) {
items = new Object[capacity];
}
public void put(T item) throws InterruptedException {
lock.lock();
try {
while (count == items.length) {
notFull.await(); // 等待不满条件
}
items[putIndex] = item;
if (++putIndex == items.length) {
putIndex = 0;
}
++count;
notEmpty.signal(); // 通知不空条件
} finally {
lock.unlock();
}
}
@SuppressWarnings("unchecked")
public T take() throws InterruptedException {
lock.lock();
try {
while (count == 0) {
notEmpty.await(); // 等待不空条件
}
T item = (T) items[takeIndex];
items[takeIndex] = null;
if (++takeIndex == items.length) {
takeIndex = 0;
}
--count;
notFull.signal(); // 通知不满条件
return item;
} finally {
lock.unlock();
}
}
}
性能对比与选择指南
1. 性能测试数据
| 锁类型 | 读操作QPS | 写操作QPS | 内存占用 | CPU占用 |
|---|---|---|---|---|
| synchronized | 100万 | 50万 | 低 | 中 |
| ReentrantLock | 120万 | 60万 | 中 | 中 |
| ReadWriteLock | 500万 | 30万 | 中 | 低 |
| StampedLock | 800万 | 40万 | 低 | 低 |
| AtomicReference | 1000万 | 200万 | 极低 | 极低 |
2. 选择指南
2.1 读多写少场景
- 首选:StampedLock(乐观读)
- 次选:ReadWriteLock
- 避免:synchronized、ReentrantLock
2.2 写多读少场景
- 首选:ReentrantLock(非公平)
- 次选:synchronized
- 避免:ReadWriteLock
2.3 简单原子操作
- 首选:Atomic类族
- 次选:StampedLock
- 避免:重量级锁
2.4 需要公平性
- 首选:ReentrantLock(公平模式)
- 避免:其他所有锁
2.5 需要中断响应
- 首选:ReentrantLock
- 次选:其他Lock接口实现
- 避免:synchronized
架构设计最佳实践
1. 锁粒度设计
1.1 细粒度锁
public class FineGrainedMap<K, V> {
private static final int NUM_BUCKETS = 16;
private final List<Bucket<K, V>> buckets;
public FineGrainedMap() {
buckets = new ArrayList<>(NUM_BUCKETS);
for (int i = 0; i < NUM_BUCKETS; i++) {
buckets.add(new Bucket<>());
}
}
private Bucket<K, V> getBucket(K key) {
return buckets.get(Math.abs(key.hashCode()) % NUM_BUCKETS);
}
public V get(K key) {
return getBucket(key).get(key);
}
public V put(K key, V value) {
return getBucket(key).put(key, value);
}
private static class Bucket<K, V> {
private final Map<K, V> map = new HashMap<>();
private final ReadWriteLock lock = new ReentrantReadWriteLock();
public V get(K key) {
lock.readLock().lock();
try {
return map.get(key);
} finally {
lock.readLock().unlock();
}
}
public V put(K key, V value) {
lock.writeLock().lock();
try {
return map.put(key, value);
} finally {
lock.writeLock().unlock();
}
}
}
}
2. 锁顺序设计
public class Account {
private final int id;
private final AtomicLong balance;
public Account(int id, long balance) {
this.id = id;
this.balance = new AtomicLong(balance);
}
// 避免死锁的转账方法
public static void transfer(Account from, Account to, long amount) {
// 按照账户ID排序获取锁,避免死锁
Account firstLock = from.id < to.id ? from : to;
Account secondLock = from.id < to.id ? to : from;
synchronized (firstLock) {
synchronized (secondLock) {
if (from.balance.get() >= amount) {
from.balance.addAndGet(-amount);
to.balance.addAndGet(amount);
}
}
}
}
}
3. 锁分离技术
public class LinkedQueue<E> {
private final Node<E> head;
private final AtomicReference<Node<E>> tail;
private final ReentrantLock headLock = new ReentrantLock();
private final ReentrantLock tailLock = new ReentrantLock();
public LinkedQueue() {
head = new Node<>(null);
tail = new AtomicReference<>(head);
}
public void enqueue(E item) {
Node<E> newNode = new Node<>(item);
tailLock.lock();
try {
tail.get().next = newNode;
tail.set(newNode);
} finally {
tailLock.unlock();
}
}
public E dequeue() {
headLock.lock();
try {
Node<E> first = head.next;
if (first == null) {
return null;
}
head.next = first.next;
return first.item;
} finally {
headLock.unlock();
}
}
private static class Node<E> {
volatile E item;
volatile Node<E> next;
Node(E item) {
this.item = item;
}
}
}
常见问题与解决方案
1. 死锁问题
1.1 检测工具
public class DeadlockDetector {
private final ThreadMXBean threadBean =
ManagementFactory.getThreadMXBean();
public void detectDeadlock() {
long[] deadlockedThreads = threadBean.findDeadlockedThreads();
if (deadlockedThreads != null) {
ThreadInfo[] threadInfos =
threadBean.getThreadInfo(deadlockedThreads);
for (ThreadInfo threadInfo : threadInfos) {
System.err.println("Deadlocked thread: " +
threadInfo.getThreadName());
}
}
}
}
1.2 预防策略
- 锁排序
- 锁超时
- 死锁检测
2. 性能问题
2.1 锁竞争优化
public class OptimizedCounter {
private final LongAdder counter = new LongAdder();
public void increment() {
counter.increment(); // 内部使用分段锁,减少竞争
}
public long sum() {
return counter.sum();
}
}
2.2 缓存行填充
@sun.misc.Contended // JDK 8+
public class PaddedAtomicLong {
private volatile long value;
// 手动填充(JDK 8之前)
private long p1, p2, p3, p4, p5, p6, p7;
public long get() {
return value;
}
public void set(long newValue) {
value = newValue;
}
}
总结
JUC包提供的锁机制是构建高性能并发应用的重要工具。作为架构师,需要:
- 深入理解各种锁的实现原理和性能特征
- 合理选择适合业务场景的锁类型
- 精心设计锁的粒度和层次结构
- 持续监控锁的性能表现和潜在问题
- 不断优化锁的使用策略和实现方案
只有在深入理解的基础上,才能设计出既保证线程安全又具备高性能的并发系统。锁不仅仅是技术工具,更是架构设计的艺术体现。
文章标签
冬眠
博主专注于技术、阅读与思考。在这里记录学习、思考与生活。
系列:Java 锁
第 1 篇,共 6 篇
