Lock简介以及和synchronized的比较
在锁定操作上,相对于使用synchronized方法和语句快,Lock能获得的更广泛的锁定操作。它们允许更灵活的结构,可能具有完全不同的属性,并且可能支持多个关联的Condition对象。
锁是一种用于控制多个线程对共享资源的访问的工具。通常,锁提供对共享资源的独占访问:一次只有一个线程可以获取锁,并且对共享资源的所有访问都需要首先获取锁。但是,某些锁可能允许并发访问共享资源,例如ReadWriteLock的读锁定。
synchronized方法或语句的使用提供了对与每个对象关联的隐式监视器锁的访问,但强制所有锁获取和释放以块结构方式发生:当获取多个锁时,它们必须是以相反的顺序释放,并且所有锁必须在获取它们的相同词法范围内释放。
虽然synchronized方法和语句块的作用域机制使得使用锁进行编程变得更加容易,并且有助于避免许多涉及锁的常见编程错误,但有时需要更灵活地使用锁的办法。例如,用于并发遍历访问的数据结构的一些算法需要锁的交接,或者“链锁定”:你获得节点A的锁,然后是节点B,然后释放A并获得C,然后释放B并获得D,依此类推。 Lock接口的实现允许使用这种技术,允许在不同的范围内获取和释放锁,并允许以任何顺序获取和释放多个锁。
这种增加的灵活性也带来了更多需要注意的地方。因为不再使用synchronized方法和语句块,锁不会自动释放。大多情况下都需要使用以下方式:
缺少块结构锁定会删除使用{@code synchronized}方法和语句发生的锁的自动释放。在大多数情况下,应使用以下习语:
Lock l = ...;
l.lock();
try {
//访问受此锁保护的资源
} finally {
l.unlock();
}
当锁定和解锁发生在不同的范围内时,必须注意确保在保持锁定时执行的所有代码都受try-finally或try-catch保护,以确保在必要时释放锁定。
相比于synchronized方法和语句块,Lock提供了更多的的附加功能,通过提供获取锁的非阻塞尝试——ryLock,尝试获取可以中断的锁——lockInterruptibly,以及尝试获取可能超时的锁——tryLock(long,TimeUnit)。
另外,Lock类还有一些不同的行为和语义,例如保证排序,非重入使用或死锁检测。
需要注意的是,Lock的实例只是普通对象,可以在synchronized语句中用作目标,即synchronized(lock),这种用法和lock的锁定没有联系。为避免混淆,建议您不要以这种方式使用Lock实例,除非在他们自己的实现中。
内存同步
TODO
注意事项
三种形式的锁的获取(可中断,不可中断和定时)可能在性能特征、排序保证或其他质量方面有所不同。在特定的锁的实现类中,可能无法终端ongoing状态的锁。因此,不需要实现为所有三种形式的锁获取定义完全相同的保证或语义,也不需要支持正在进行的锁获取的中断。
需要一种实现来清楚地记录每种锁定方法提供的语义和保证。它还必须遵守此接口中定义的中断语义,以支持锁获取的中断:完全或仅在方法入口上。
由于中断通常意味着取消,并且中断检查通常不常见,因此实现可能有利于响应正常方法返回的中断。即使可以显示在另一个操作可能已取消阻塞线程之后发生中断,也是如此。实现应记录此行为。
接口方法
方法列表
void lock();
-
获取锁。
-
如果锁定不可用,则当前线程不再参与线程调度,并且在获取锁定之前处于休眠状态。
-
使用方式:
void lockInterruptibly() throws InterruptedException;
- 获取锁,除非线程被中断。
- 如果锁可用,立即返回
- 如果锁定不可用,则当前线程不再参与线程调度,并且在获取锁定之前处于休眠状态,除非发生以下事情:
- 锁被当前线程获取。
- 或者当前线程被其他线程中断,并且锁的获取支持中断。
boolean tryLock();
- 锁空闲则获取锁:
- 锁可用,返回true
- 锁不可用,返回false
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
-
获取锁,在给定时间内,在线程未被中断时。
-
可以获取:立即返回true
-
不可获取:进入禁用调度状态,休眠直到以下事情发生:
- 获取到锁
- 被中断
- 时间到
时间到则返回false
-
void unlock();
- 释放锁
Condition newCondition();
- returns a new Condition instance that is bound to this lock instance.
几种获取锁的方法的比较
- Lock() 拿不到lock就不罢休,不然线程就一直block。 比较无赖的做法。
- tryLock() 马上返回,拿到lock就返回true,不然返回false。 比较潇洒的做法。
- 带时间限制的tryLock 比较聪明的做法,相较于tryLock()——支持中断
- lockInterruptibly() 相较于lock(),支持中断
使用方式
- lock():
Lock l = ...;
l.lock();
try {
//访问受此锁保护的资源
} finally {
l.unlock();
}
- tryLock():
Lock lock = ...;
if(lock.tryLock()) {
try{
//处理任务
}catch(Exception ex){
}finally{
lock.unlock(); //释放锁
}
}else {
//如果不能获取锁,则直接做其他事情
}
- lockInterruptibly():
public void method() throws InterruptedException {
lock.lockInterruptibly();
try {
//.....
}
finally {
lock.unlock();
}
}