Java并发编程中的七种锁机制详解
为何需要锁机制
在多线程环境中,当多个线程同时访问共享资源时,可能会引发数据不一致的问题。例如经典的i++操作,在并发环境下可能得不到预期结果。锁的作用类似于现实生活中的权限管理,它确保同一时刻只有一个线程能够访问特定资源,从而保障数据的安全性。
七种常见锁机制解析
1. synchronized内置锁
synchronized是Java中最基础的同步机制,它可以修饰方法或代码块。当线程进入被synchronized修饰的区域时会自动获得锁,退出时自动释放锁。
public class SyncDemo {
public synchronized void doSomething() {
System.out.println(Thread.currentThread().getName() + " 正在执行");
}
public void doAnotherThing() {
synchronized(this) {
System.out.println(Thread.currentThread().getName() + " 正在执行");
}
}
}
2. ReentrantLock可重入锁
ReentrantLock提供了比synchronized更灵活的锁定机制,支持公平锁、超时获取锁等功能。需要注意的是,必须手动释放锁以避免死锁。
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockDemo {
private final ReentrantLock lock = new ReentrantLock();
public void performTask() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " 获得锁");
nestedOperation();
} finally {
lock.unlock();
System.out.println(Thread.currentThread().getName() + " 释放锁");
}
}
private void nestedOperation() {
lock.lock();
try {
System.out.println("嵌套操作");
} finally {
lock.unlock();
}
}
}
3. ReadWriteLock读写锁
读写锁将锁分为读锁和写锁两种类型。允许多个线程同时进行读操作,但在写操作期间禁止任何读写操作。
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class RWLDemo {
private final ReadWriteLock rwl = new ReentrantReadWriteLock();
private int value = 0;
public int getValue() {
rwl.readLock().lock();
try {
return value;
} finally {
rwl.readLock().unlock();
}
}
public void setValue(int val) {
rwl.writeLock().lock();
try {
value = val;
} finally {
rwl.writeLock().unlock();
}
}
}
4. StampedLock高性能锁
StampedLock是一种更为高效的读写锁实现,支持乐观读模式。在这种模式下,读操作不需要阻塞写操作,但需要验证数据一致性。
import java.util.concurrent.locks.StampedLock;
public class StampedLockDemo {
private final StampedLock sl = new StampedLock();
private double x, y;
public void move(double dx, double dy) {
long stamp = sl.writeLock();
try {
x += dx;
y += dy;
} finally {
sl.unlockWrite(stamp);
}
}
public double getDistance() {
long stamp = sl.tryOptimisticRead();
double currentX = x, currentY = y;
if (!sl.validate(stamp)) {
stamp = sl.readLock();
try {
currentX = x;
currentY = y;
} finally {
sl.unlockRead(stamp);
}
}
return Math.sqrt(currentX * currentX + currentY * currentY);
}
}
5. Semaphore信号量
Semaphore用于控制同时访问特定资源的线程数量。通过许可令牌的方式限制并发访问数。
import java.util.concurrent.Semaphore;
public class SemaphoreDemo {
private final Semaphore sem = new Semaphore(3);
public void useResource() {
try {
sem.acquire();
System.out.println(Thread.currentThread().getName() + " 使用资源");
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
sem.release();
System.out.println(Thread.currentThread().getName() + " 释放资源");
}
}
}
6. CountDownLatch倒计时器
CountDownLatch允许一个或多个线程等待其他线程完成操作。初始化时设定计数值,每当有线程完成任务就调用countDown()减少计数,当计数为零时唤醒等待线程。
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " 完成任务");
latch.countDown();
}).start();
}
latch.await();
System.out.println("所有任务已完成");
}
}
7. CyclicBarrier循环屏障
CyclicBarrier使得一组线程相互等待,直到所有线程都到达屏障点后再继续执行。与CountDownLatch不同的是,它可以重复使用。
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo {
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(3, () ->
System.out.println("所有参与者已就位"));
for (int i = 0; i < 3; i++) {
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " 到达屏障");
barrier.await();
System.out.println(Thread.currentThread().getName() + " 继续执行");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
锁机制选择指南
| 锁类型 | 主要特征 | 推荐使用场景 | 优势 | 劣势 |
|---|---|---|---|---|
| synchronized | 自动加锁解锁 | 基本同步需求 | 语法简单,自动管理 | 功能有限 |
| ReentrantLock | 手动控制锁 | 复杂同步逻辑 | 支持超时、中断等 | 需手动释放 |
| ReadWriteLock | 读写分离 | 读多写少场景 | 提升读并发性能 | 写操作影响读 |
| StampedLock | 乐观读支持 | 极高读频场景 | 极致读性能 | 逻辑相对复杂 |
| Semaphore | 并发数量控制 | 资源池、限流 | 精确控制并发数 | 非直接同步方案 |
| CountDownLatch | 一次性等待 | 前置任务等待 | 简单高效 | 不可重复使用 |
| CyclicBarrier | 循环等待集合 | 阶段性协作任务 | 可重复利用 | 要求全员参与 |