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