Lock和synchronized是最常见的锁,用于控制对共享资源的访问。Lock不是用来代替synchronized的,而是synchronized不满足要求时,提供更高级的功能。Lock接口常见实现类是ReentrantLock和ReadWriteLock。
synchronized的缺点,参见之前的小节。
- 锁效率低
- 锁的释放情况少。synchronized监视器锁在方法或代码块正常退出和异常时释放锁。
- 试图获得锁时不能设置超时、不能中断一个正在试图获得锁的线程。
- 不够灵活
相对于读写锁,加锁和释放的时机单一,每个锁只有单一条件(锁定某个对象),可能不够。 - 无法知道是否成功获取到锁。
例如Lock的tryLock方法
1 Lock接口分析
public interface Lock {
// 获取锁
void lock();
// 获取锁时可以被中断
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
// 超时就放弃
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
// 解锁
void unlock();
Condition newCondition();
}
- Lock不会像synchronized在异常时释放锁,因此需要在finally调用unlock,确保锁被释放。
- lock()方法不能被中断,一旦死锁,会陷入永久等待。
- tryLock()用来尝试获取锁,如果当前锁未被其他线程占用,则获取锁,并返回true;否则返回false。方法立即返回,即使拿不到锁也会等待。
tryLock代码规范,参考注释
Lock lock = new ReentrantLock();
if(lock.tryLock()) {
try {
} finally {
lock.unlock();
}
}
线程等待Lock锁时是WAITING状态,不可能是BLOCKED.
2 Lock的可见性保证
Lock的可见性与synchronized相同,可以参考happens-before原则。解锁操作对之后的加锁操作可见。
lock和unlock对齐内存交互操作中的lock和unlock操作。