Java中的锁机制:synchronized vs ReentrantLock,如何选择?
在Java中,synchronized
和 ReentrantLock
是两种常用的锁机制,用于实现线程同步。选择哪种机制取决于具体的应用场景和需求。以下是它们的对比和选择建议:
1. synchronized
-
内置支持:
synchronized
是Java语言内置的关键字,使用简单,无需额外引入类库。 -
自动释放锁:
synchronized
在代码块执行完毕或发生异常时,会自动释放锁,减少了锁泄漏的风险。 -
不可中断:
synchronized
不支持中断,一旦线程进入等待锁的状态,就无法被中断。 -
非公平锁:
synchronized
默认是非公平锁,无法指定公平性。 -
性能:在Java 6及以后的版本中,
synchronized
的性能有了显著提升,适用于大多数场景。
适用场景:
-
简单的同步需求,代码量较少。
-
不需要复杂的锁控制(如可中断、公平锁等)。
-
对性能要求不是特别苛刻的场景。
2. ReentrantLock
-
灵活性:
ReentrantLock
提供了更多的功能,如可中断锁、公平锁、尝试获取锁等。 -
可中断:
ReentrantLock
支持中断等待锁的线程,提供了lockInterruptibly()
方法。 -
公平锁:
ReentrantLock
可以指定是否为公平锁,公平锁会按照请求锁的顺序分配锁。 -
条件变量:
ReentrantLock
提供了Condition
类,可以实现更复杂的线程通信。 -
手动释放锁:
ReentrantLock
需要手动调用unlock()
方法释放锁,容易忘记释放锁,导致死锁。
适用场景:
-
需要更复杂的锁控制,如可中断、公平锁等。
-
需要实现复杂的线程通信,使用
Condition
。 -
对性能有较高要求,且需要更细粒度的锁控制。
选择建议
-
简单场景:如果只是简单的同步需求,推荐使用
synchronized
,因为它使用简单,且不易出错。 -
复杂场景:如果需要更复杂的锁控制,如可中断、公平锁、条件变量等,推荐使用
ReentrantLock
。 -
性能考虑:在大多数情况下,
synchronized
的性能已经足够好,但在高并发场景下,ReentrantLock
可能提供更好的性能。
示例代码
synchronized
示例
public class SynchronizedExample {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
ReentrantLock
示例
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private int count = 0;
private Lock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public int getCount() {
lock.lock();
try {
return count;
} finally {
lock.unlock();
}
}
}
总结
-
synchronized
:简单易用,适合大多数场景。 -
ReentrantLock
:功能强大,适合需要复杂锁控制的场景。
根据具体需求选择合适的锁机制,可以提高代码的可维护性和性能。