Java 多线程进阶讲解与各种锁机制

在 Java 多线程编程中,锁机制是保证线程安全的重要手段。本文将深入探讨 Java 中的各种锁机制,包括 synchronizedReentrantLockReadWriteLockStampedLock 等,并通过代码示例详细讲解其用法。


1. synchronized 关键字

synchronized 是 Java 中最基本的锁机制,可以修饰方法或代码块,确保同一时间只有一个线程可以访问共享资源。

修饰方法

1
2
3
4
5
6
7
8
9
10
11
class Counter {
private int count = 0;

public synchronized void increment() { // 使用 synchronized 修饰方法
count++; // 对共享变量进行操作
}

public int getCount() {
return count;
}
}

修饰代码块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Counter {
private int count = 0;
private final Object lock = new Object(); // 定义一个锁对象

public void increment() {
synchronized (lock) { // 使用 synchronized 修饰代码块
count++; // 对共享变量进行操作
}
}

public int getCount() {
return count;
}
}

特点:

简单易用,无需手动释放锁。

性能较低,不适合高并发场景。


2. ReentrantLock

ReentrantLock 是 java.util.concurrent.locks 包中的一个类,提供了比 synchronized 更灵活的锁机制。

基本用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class Counter {
private int count = 0;
private final Lock lock = new ReentrantLock(); // 创建 ReentrantLock 对象

public void increment() {
lock.lock(); // 加锁
try {
count++; // 对共享变量进行操作
} finally {
lock.unlock(); // 释放锁
}
}

public int getCount() {
return count;
}
}

可重入性

ReentrantLock 是可重入锁,同一个线程可以多次获取同一把锁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void methodA() {
lock.lock(); // 第一次加锁
try {
methodB(); // 调用另一个需要加锁的方法
} finally {
lock.unlock(); // 释放锁
}
}

public void methodB() {
lock.lock(); // 第二次加锁
try {
// 执行操作
} finally {
lock.unlock(); // 释放锁
}
}

特点:

支持公平锁和非公平锁。

支持尝试获取锁(tryLock)和超时机制。

需要手动释放锁。


3. ReadWriteLock

ReadWriteLock 是一种读写分离锁,允许多个读线程同时访问共享资源,但写线程独占资源。

基本用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

class SharedData {
private int data = 0;
private final ReadWriteLock rwLock = new ReentrantReadWriteLock(); // 创建读写锁

public void write(int value) {
rwLock.writeLock().lock(); // 获取写锁
try {
data = value; // 写操作
} finally {
rwLock.writeLock().unlock(); // 释放写锁
}
}

public int read() {
rwLock.readLock().lock(); // 获取读锁
try {
return data; // 读操作
} finally {
rwLock.readLock().unlock(); // 释放读锁
}
}
}

特点:

读操作可以并发执行,写操作独占资源。

适合读多写少的场景。


4. StampedLock

StampedLock 是 Java 8 引入的一种新型锁,提供了更高的性能,支持乐观读锁。

基本用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import java.util.concurrent.locks.StampedLock;

class SharedData {
private int data = 0;
private final StampedLock stampedLock = new StampedLock(); // 创建 StampedLock 对象

public void write(int value) {
long stamp = stampedLock.writeLock(); // 获取写锁
try {
data = value; // 写操作
} finally {
stampedLock.unlockWrite(stamp); // 释放写锁
}
}

public int read() {
long stamp = stampedLock.tryOptimisticRead(); // 尝试获取乐观读锁
int currentData = data; // 读取数据
if (!stampedLock.validate(stamp)) { // 检查数据是否被修改
stamp = stampedLock.readLock(); // 获取悲观读锁
try {
currentData = data; // 重新读取数据
} finally {
stampedLock.unlockRead(stamp); // 释放读锁
}
}
return currentData;
}
}

特点:

支持乐观读锁,减少锁竞争。

性能优于 ReadWriteLock,但使用更复杂。


5. 锁的性能比较

锁类型 特点 适用场景
synchronized 简单易用,性能较低 低并发场景
ReentrantLock 灵活,支持公平锁、非公平锁、超时机制 高并发场景
ReadWriteLock 读写分离,读操作并发执行 读多写少的场景
StampedLock 高性能,支持乐观读锁 读多写少且对性能要求高的场景


6. 总结

synchronized 是最基本的锁机制,适合简单的并发场景。

ReentrantLock 提供了更灵活的锁控制,适合高并发场景。

ReadWriteLock 实现了读写分离,适合读多写少的场