1. Lock类的说明
- Lock 的实现类提供了比synchronized方法和语句的更多的扩展的锁操作
他们允许更加灵活多变的结构,可能有相当不同的属性,可能支持多种的关联Condition对象 - 锁是一种工具,用于控制多个线程对共享资源的访问。通常,锁提供对共享资源的独占访问(exclusive access):一次只有一个线程可以获取锁,并且所有对共享资源的访问都要求首先获取锁。然而,某些锁可能允许对共享资源的并发访问,例如ReadWriteLock中的读锁。
- 使用synchronized方法或语句提供了对每个对象关联的隐式监视器锁(implicit monitor )的访问,但强制所有的锁获取和释放都必须以块结构的方式进行:当获取多个锁时,它们必须按相反的顺序释放,并且所有锁都必须在获取它们的同一词法范围内释放。
- 虽然对于synchronized方法和语句的范围机制使得使用监视器锁编程变得更加容易,并且有助于避免许多涉及锁的常见编程错误,但在某些情况下,你需要以更加灵活的方式来处理锁。例如,有些算法用于遍历并发访问的数据结构时,需要使用“hand-over-hand”或“chain locking”的方式:你先获取节点A的锁,然后是节点B的锁,接着释放A并获取C的锁,再释放B并获取D的锁,依此类推。实现Lock接口的类通过允许在不同的范围内获取和释放锁,以及允许多个锁以任意顺序获取和释放,从而支持使用这样的技术。
- 随着灵活性的增加,也带来了额外的责任。块结构锁定的缺失意味着不会像synchronized方法和语句那样自动释放锁。在大多数情况下,应该使用以下模式
当锁的获取和释放发生在不同的范围内时,必须小心确保所有在持有锁期间执行的代码都受到try-finally或try-catch的保护,以确保在必要时能够释放锁Lock l = ...; l.lock(); try { // access the resource protected by this lock } finally { l.unlock(); }}
6. Lock接口的实现通过提供以下功能,比使用synchronized方法和语句提供了更多的灵活性:非阻塞尝试获取锁tryLock(), 可以被中断的锁获取尝试 lockInterruptibly,可以设置超时时间的锁获取尝试tryLock(long, TimeUnit).
7. Lock类还可以提供与隐式监视器锁非常不同的行为和语义,例如保证的顺序、不可重入的使用或死锁检测。如果某个实现提供了这种专门的语义,那么该实现必须在文档中说明这些语义
8. 请注意,Lock实例只是普通的对象,它们自身可以用作synchronized语句中的目标。获取Lock实例的监视器锁与调用该实例的任何lock方法之间没有明确的关系。为了避免混淆,建议你永远不要以这种方式使用Lock实例,除非是在它们自己的实现中。
9. 除非另有说明,对任何参数传递null值都将导致抛出NullPointerException
Memory Synchronization
- 所有Lock实现必须强制执行与内置监视器锁相同的内存同步语义,如《Java语言规范》第17章所述:
- 成功的lock操作具有与成功的Lock操作相同的内存同步效果。
- 成功的unlock操作具有与成功的Unlock操作相同的内存同步效果。
- 不成功的锁定和解锁操作,以及可重入的锁定/解锁操作,不需要任何内存同步效果
Implementation Considerations
- 三种形式的锁获取(可中断的、不可中断的和定时的)可能在性能特性、顺序保证或其他实现质量方面有所不同。此外,中断正在进行(ongoing)的锁获取的能力可能在给定的Lock类中不可用。因此,实现并不需要为所有三种形式的锁获取定义完全相同的保证或语义,也不需要支持对正在进行的锁获取的中断。实现必须清楚地记录每种锁定方法提供的语义和保证。它还必须遵守本接口中定义的中断语义,即在支持锁获取中断的情况下,要么完全支持,要么仅在方法入口处支持
- 由于中断通常意味着取消,并且中断检查往往不频繁,因此实现可以优先响应中断而不是正常的方法返回。即使可以证明中断发生在另一个动作可能已经解除线程阻塞之后,这也是正确的。实现应该记录这种行为
- 参考:ReentrantLock, Condition,ReadWriteLock , 17.4 Memory Model
public interface Lock {
/**
* 获取锁 Acquires the lock.
* 如果锁不可用,则当前线程将被禁用以进行线程调度,并保持休眠(lies dormant)状态,直到获取到锁为止。
*
* 实现考虑:
* 一个Lock实现可能能够检测到锁的错误(erroneous )使用,
* 例如可能导致死锁的调用,并在这种情况下抛出一个(未检查的)异常。
* 这种情形以及异常类型必须由该Lock实现文档说明。
*
*/
void lock();
/**
*
* 如果当前线程没有被中断,则获取锁
*
* 如果锁可用,则立即获取并返回。
* 如果锁不可用,则当前线程将被禁用以进行线程调度,并保持休眠状态,直到以下两种情况之一发生:
* 1. 当前线程获取到锁;或
* 2. 其他线程中断了当前线程,并且支持中断锁的获取。
*
* 如果当前线程
* 1. 如果在进入此方法时当前线程的interrupted 状态已被设置,或者
* 2. 在获取锁的过程中当前线程被中断,并且支持中断锁的获取
* 则抛出InterruptedException,并且当前线程的中断状态将被清除
*
* 实现考虑:
* 1. 在某些实现中,中断锁的获取可能不可行,即使可行也可能是一个昂贵的操作。
* 程序员应该意识到这种情况的可能性。 实现应该记录这种情况。
* 2. 实现可以优先响应中断而不是正常的方法返回。
* 3. 一个Lock实现可能能够检测到锁的错误使用,例如可能导致死锁的调用,并在这种情况下抛出一个(未检查的)异常。
* 这种情形以及异常类型必须由该Lock实现文档说明。
*
* @throws InterruptedException 如果当前线程在获取锁的过程中(并且支持中断锁的获取)被interrupted
*/
void lockInterruptibly() throws InterruptedException;
/**
* 仅在调用时锁是空闲的情况下获取锁
*
* 如果锁可用,则立即获取并返回true。
* 如果锁不可用,则立即返回false。
* 典型用法:
*
* Lock lock = ...;
* if (lock.tryLock()) {
* try {
* // manipulate protected state
* } finally {
* lock.unlock();
* }
* } else {
* // perform alternative actions
* }}
* 这种用法确保了如果锁被成功获取,则会在finally块中释放锁;如果没有获取到锁,则不会尝试释放锁。
*
* @return 如果锁被成功获取,返回true。否则返回false。
*/
boolean tryLock();
/**
* 在给定的等待时间内获取锁(如果锁是空闲的并且当前线程没有被中断)
* 如果锁可用,该方法会立即返回true。
* 如果锁不可用,则当前线程将变为不可调度状态并进入休眠,直到以下三种情况之一发生:
* 1. 当前线程获取到锁;
* 2. 其他线程中断了当前线程,并且支持中断锁的获取;
* 3. 指定的等待时间已过。
* 如果锁被成功获取,则返回true
*
* 如果当前线程:
* 在调用此方法时已经设置了中断状态;或者
* 在获取锁的过程中被中断,并且支持中断锁的获取, 则抛出InterruptedException异常,并清除当前线程的中断状态。
* 如果指定的等待时间已过,
* 则返回false。
*
* 如果等待时间小于或等于零,该方法不会等待。
*
* 实现考虑:
* 1. 在某些实现中,中断锁的获取可能不可行,即使可行也可能是一个昂贵的操作。
* 程序员应该意识到这种情况的可能性。 实现应该记录这种情况。
* 2. 实现可以优先响应中断而不是正常的方法返回或报告超时。
* 3. 一个Lock实现可能能够检测到锁的错误使用,例如可能导致死锁的调用,并在这种情况下抛出一个(未检查的)异常。
* 这种情形以及异常类型必须由该Lock实现文档说明。
*
*
* @param time 等待锁的最大时间
* @param unit time参数的时间单位。
* @return 如果锁被成功获取,返回true。如果等待时间已过而未能获取到锁,返回false。
*
* @throws InterruptedException 如果当前线程在获取锁的过程中(并且支持中断锁的获取)被interrupted
*/
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
/**
* 释放锁
* 实现考虑:
* Lock实现通常会对哪个线程可以释放锁进行限制(通常是只有锁的持有者才能释放它),
* 如果违反了这些限制,可能会抛出一个未检查的异常。
* 任何限制和异常类型都必须由该Lock实现进行文档说明。
*
*/
void unlock();
/**
* 返回一个新的与当前Lock实例绑定的Condition实例
*
* 在等待条件之前,当前线程必须持有锁。
*
* 调用Condition#await()方法将原子性地在等待前释放锁,并在等待返回前重新获取锁。
*
* 实现考虑:
* Condition实例的确切操作取决于Lock的实现,并且必须由该实现进行文档说明。
*
* @return 返回一个新的与当前Lock实例关联的Condition实例
* @throws 如果这个Lock实现不支持条件,则抛出UnsupportedOperationException。
*/
Condition newCondition();
}