synchronized
和 ReentrantLock
两者的功能是一致的,都是 Java 中用于管理并发和同步机制的,但它们两者之间还是存在一些差异的。
用法不同
synchronized
可用来修饰普通方法、静态方法和代码块
public synchronized void test() {
//...
}
public void test() {
synchronized (this) {
//...
}
}
ReentrantLock
只能用在代码块上。
private final ReentrantLock lock = new ReentrantLock();
public void test() {
// 加锁
lock.lock();
try {
// ...
} finally {
// 释放锁
lock.unlock();
}
}
获取锁和释放锁机制不同
synchronized
的获取锁和释放锁是自动的,当进入synchronized
修饰的方法或者方法体内,会自动获取锁,当执行完成后会自动释放锁。
ReentrantLock
需要显示地获取锁和释放锁。
由于ReentrantLock
需要显示调用 unlock()
释放锁,所以一定千万要主要不能漏调用这个方法,同事也需要将该方法调用逻辑放在 finally 中。
公平性
synchronized
不保证公平性。所以,线程获取锁的顺序是不可预测的,不能保证先请求锁的线程先获取锁。ReentrantLock
支持公平锁和非公平锁,如果设置为公平锁,则ReentrantLock
能保证先请求锁的会先获取锁。
响应中断
synchronized
不能响应中断。ReentrantLock
提供了lockInterruptibly()
来支持响应中断的能力,可以使线程在等待锁的过程中响应中断。
用代码演示下:
public class ReentrantLockTest {
private final Lock lock = new ReentrantLock();
public void lockInterruptiblyTest() {
try {
lock.lockInterruptibly();
try {
System.out.println(Thread.currentThread().getName() + "-成功获取锁;;;" + LocalTime.now());
Thread.sleep(5000);
} finally {
lock.unlock();
System.out.println(Thread.currentThread().getName() + "-成功释放锁;;;" + LocalTime.now());
}
} catch (InterruptedException e) {
// 响应中断
System.out.println(Thread.currentThread().getName() + "-响应中断;;;" + LocalTime.now());
}
}
public static void main(String[] args) throws InterruptedException {
ReentrantLockTest lockTest = new ReentrantLockTest();
Thread thread01 = new Thread(lockTest::lockInterruptiblyTest);
Thread thread02 = new Thread(lockTest::lockInterruptiblyTest);
// thread01 先启动,获取锁
thread01.start();
// sleep(1000) 等待线程 1 先获取锁
Thread.sleep(1000);
// thread02 后启动
thread02.start();
// sleep(1000) 后 ,thread02 中断
Thread.sleep(1000);
thread02.interrupt();
}
}
执行结果:
底层实现
synchronized
是 JVM 层面通过监视器(Monitor)实现的。ReentrantLock
则是 Java 层次的,是通过 AQS 的 API 实现的。
功能丰富性
ReentrantLock
提供了比 synchronized
更多的功能,比如支持获取锁响应中断、获取锁超时,还提供了一个写 API 如getHoldCount()
、isHeldByCurrentThread()
、getQueueLength()
等,这些可以帮助我们详细管理锁的状态。