1. 锁的粒度选择
- 粗粒度锁:
- 分析:粗粒度锁会锁定较大范围的代码块或方法,在高并发场景下,会导致大量线程竞争同一把锁,造成线程阻塞,降低系统并发性能。但如果锁保护的代码执行时间较长,粗粒度锁可能减少加锁解锁的开销。
- 示例:
public class CoarseGrainedLockExample {
private static final Object lock = new Object();
public void doSomething() {
synchronized (lock) {
// 执行一系列操作,可能包含多个独立操作,但都被锁保护
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
// 模拟一些计算
}
long endTime = System.currentTimeMillis();
System.out.println("Coarse grained lock operation took " + (endTime - startTime) + " ms");
}
}
}
- 细粒度锁:
- 分析:细粒度锁只锁定关键的、可能产生竞争的代码片段,能提高并发性能,因为不同线程可以同时访问不同的细粒度锁保护的区域。但如果锁的粒度太细,加锁解锁的开销可能会增加。
- 示例:
public class FineGrainedLockExample {
private static final Object lock1 = new Object();
private static final Object lock2 = new Object();
public void doPart1() {
synchronized (lock1) {
long startTime = System.currentTimeMillis();
for (int i = 0; i < 500000; i++) {
// 模拟一些计算
}
long endTime = System.currentTimeMillis();
System.out.println("Part 1 with fine grained lock took " + (endTime - startTime) + " ms");
}
}
public void doPart2() {
synchronized (lock2) {
long startTime = System.currentTimeMillis();
for (int i = 0; i < 500000; i++) {
// 模拟一些计算
}
long endTime = System.currentTimeMillis();
System.out.println("Part 2 with fine grained lock took " + (endTime - startTime) + " ms");
}
}
}
2. 锁的类型对性能的影响
- 内置锁(synchronized):
- 分析:synchronized 是Java内置的同步机制,它是一种悲观锁,即总是假设最坏的情况,每次获取锁时都认为其他线程会竞争,所以会进行加锁操作。在JDK 1.6 之前性能较差,因为竞争激烈时会导致线程频繁阻塞和唤醒。但在JDK 1.6 之后,引入了偏向锁、轻量级锁等优化机制,性能有了较大提升。
- 示例:
public class SynchronizedExample {
public synchronized void syncMethod() {
// 同步方法
}
public void syncBlock() {
synchronized (this) {
// 同步块
}
}
}
- ReentrantLock:
- 分析:ReentrantLock 是Java.util.concurrent.locks包下的显式锁,它提供了比 synchronized 更灵活的锁控制,比如可中断的获取锁、公平锁与非公平锁的选择等。在竞争激烈的场景下,ReentrantLock 的性能可能优于 synchronized,因为它可以使用非公平锁来减少线程切换的开销,但如果需要公平性,公平锁会增加性能开销。
- 示例:
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private static final ReentrantLock lock = new ReentrantLock();
public void doWork() {
lock.lock();
try {
// 业务逻辑
} finally {
lock.unlock();
}
}
}
- 读写锁(ReentrantReadWriteLock):
- 分析:适用于读多写少的场景。它将锁分为读锁和写锁,允许多个线程同时获取读锁,但只允许一个线程获取写锁。读锁之间不互斥,提高了读操作的并发性能。如果写操作频繁,可能会导致读线程长时间等待。
- 示例:
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockExample {
private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private static final ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
private static final ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
public void readData() {
readLock.lock();
try {
// 读操作
} finally {
readLock.unlock();
}
}
public void writeData() {
writeLock.lock();
try {
// 写操作
} finally {
writeLock.unlock();
}
}
}
3. 其他要点
- 减少锁的持有时间:尽量将不需要锁保护的代码移出同步块或同步方法,减少线程持有锁的时间,从而降低锁竞争。
- 避免死锁:在使用多个锁时,要按照一定的顺序获取锁,避免形成死锁。例如在两个线程分别获取锁A和锁B时,如果都试图获取对方持有的锁,就可能死锁。可以规定统一的获取锁顺序(如先获取锁A再获取锁B)来避免这种情况。