juc并发包中locks包下lock类的理解(1)

101 阅读6分钟

1、对lock锁的理解

与使用synchronized方法和语句相比, Lock实现提供了更广泛的锁定操作。它们允许更灵活的结构,可能具有完全不同的属性,并且可能支持多个关联的Condition对象。

锁是一种控制多线程访问共享资源的工具。通常,锁提供对共享资源的独占访问:一次只有一个线程可以获取锁,并且对共享资源的所有访问都需要首先获取锁。但是,某些锁可能允许并发访问共享资源,例如ReadWriteLock的读锁。

synchronized方法或语句的使用提供了对与每个对象关联的隐式监视器锁的访问,但强制所有锁的获取和释放以块结构的方式发生:当获取多个锁时,它们必须以相反的顺序释放,并且所有锁必须在获得它们的相同词法范围内释放。 虽然synchronized方法和语句的作用域机制使使用监视器锁编程变得更加容易,并有助于避免许多涉及锁的常见编程错误,但在某些情况下,您需要以更灵活的方式使用锁。例如,一些遍历并发访问的数据结构的算法需要使用“hand-over-hand”或“链锁”:你获取节点 A 的锁,然后节点 B,然后释放 A 并获取 C,然后释放 B并获得 D 等等。

Lock接口的实现通过允许在不同范围内获取和释放锁以及允许以任意顺序获取和释放多个锁来启用此类技术。 随着这种灵活性的增加,额外的责任也随之而来。块结构锁定的缺失消除了synchronized方法和语句发生的锁定的自动释放。在大多数情况下,应使用以下成语:

image.png

当锁定和解锁发生在不同的范围内时,必须注意确保所有在持有锁时执行的代码都受到 try-finally 或 try-catch 的保护,以确保在必要时释放锁。

Lock实现通过提供非阻塞获取锁的尝试( tryLock() )、获取可中断锁的尝试( lockInterruptibly ,以及获取锁的尝试)提供了超过使用synchronized方法和语句的附加功能可以超时( tryLock(long, TimeUnit) )。

Lock类还可以提供与隐式监视器锁完全不同的行为和语义,例如保证排序、不可重入使用或死锁检测。如果实现提供了这种专门的语义,那么实现必须记录这些语义。

请注意, Lock实例只是普通对象,它们本身可以用作synchronized语句中的目标。获取Lock实例的监视器锁与调用该实例的任何lock方法没有指定关系。建议为避免混淆,除非在它们自己的实现中,否则不要以这种方式使用Lock实例。 除非另有说明,否则为任何参数传递null值将导致引发NullPointerException 。

所有Lock实现必须强制执行与内置监视器锁提供的相同的内存同步语义,如Java 语言规范(17.4 内存模型) 中所述: 成功的锁定操作与成功的lock操作具有相同的内存同步效果。

成功的解锁操作与成功的unlock操作具有相同的内存同步效果。

不成功的锁定和解锁操作,以及重入锁定/解锁操作,不需要任何内存同步效果。

实施注意事项 三种形式的锁获取(可中断、不可中断和定时)可能在它们的性能特征、顺序保证或其他实现质量方面有所不同。此外,中断正在进行的锁获取的能力在给定的Lock类中可能不可用。因此,实现不需要为所有三种形式的锁获取定义完全相同的保证或语义,也不需要支持正在进行的锁获取的中断。需要一个实现来清楚地记录每个锁定方法提供的语义和保证。它还必须遵守此接口中定义的中断语义,以支持获取锁的中断:完全或仅在方法入口上。

由于中断通常意味着取消,并且对中断的检查通常不常见,因此实现可以倾向于响应中断而不是正常的方法返回。即使可以证明在另一个操作可能已解除阻塞线程之后发生中断也是如此。一个实现应该记录这个行为。 自从:1.5

## void lock();

获取锁。 如果锁不可用,则当前线程将被禁用以用于线程调度目的并处于休眠状态,直到获得锁为止。 实施注意事项 Lock实现可能能够检测到锁的错误使用,例如会导致死锁的调用,并且在这种情况下可能会抛出(未经检查的)异常。该Lock实现必须记录情况和异常类型

void lockInterruptibly() throws InterruptedException;

除非当前线程被中断,否则获取锁。 如果可用,则获取锁并立即返回。如果锁不可用,则当前线程将被禁用以用于线程调度目的并处于休眠状态,直到发生以下两种情况之一: 锁被当前线程获取;或者 其他一些线程中断当前线程,支持获取锁的中断。 如果当前线程: 在进入此方法时设置其中断状态;或者 获取锁时中断,支持获取锁中断, 然后抛出InterruptedException并清除当前线程的中断状态。 实施注意事项 在某些实现中中断锁获取的能力可能是不可能的,并且如果可能的话可能是昂贵的操作。程序员应该意识到可能是这种情况。实现应该记录这种情况。 与正常方法返回相比,实现更倾向于响应中断。 Lock实现可能能够检测到锁的错误使用,例如会导致死锁的调用,并且在这种情况下可能会抛出(未经检查的)异常。该Lock实现必须记录情况和异常类型。 抛出: InterruptedException – 如果当前线程在获取锁时被中断(并且支持获取锁的中断)。

boolean tryLock();

image.png

boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

image.png

void unlock();

释放锁。 Lock实现通常会对哪个线程可以释放锁施加限制(通常只有锁的持有者可以释放它)并且如果违反限制可能会抛出(未经检查的)异常。该Lock实现必须记录任何限制和异常类型。

Condition newCondition();

返回绑定到此Lock实例的新Condition实例。 在等待条件之前,锁必须由当前线程持有。调用Condition.await()将在等待之前自动释放锁,并在等待返回之前重新获取锁。Condition实例的确切操作取决于Lock实现,并且必须由该实现记录。此Lock实例的新Condition实例 抛出:UnsupportedOperationException – 如果此Lock实现不支持条件。