JDK1.8源码解读之 Lock

323 阅读8分钟

前言

  • {@code Lock}实现提供的锁定操作比使用{@code sync}方法和语句可获得的锁定操作更广泛。
  • 它们允许更灵活的结构,可能具有完全不同的属性,并且可能支持多个关联的{@link Condition}对象。
  • 锁是一种用于控制多个线程对共享资源的访问的工具。
  • 通常,锁提供对共享资源的独占访问:一次只能有一个线程可以获取该锁,而对共享资源的所有访问都需要首先获取该锁。
  • 但是,某些锁可能允许并发访问共享资源,例如{@link ReadWriteLock}的读锁。
  • 使用{@code sync}方法或语句可访问与每个对象关联的隐式监视器锁,但强制所有锁的获取和释放以块结构的方式发生:当获取多个锁时,必须在锁中释放它们。
  • 顺序相反,所有锁必须在获得它们的相同词汇范围内释放。
  • 尽管{@code Synchronized}方法和语句的作用域机制使使用监视器锁进行编程变得容易得多,并且有助于避免许多常见的涉及锁的编程错误,但有时您需要以更灵活的方式使用锁。
  • 例如,某些用于遍历并发访问的数据结构的算法需要使用“移交”或“链锁”:您获取节点A的锁,然后获取节点B的锁,然后释放A并获取C,然后释放B并获得D等。
  • {@code Lock}接口的实现通过允许在不同范围内获取和释放锁,并允许以任意顺序获取和释放多个锁,从而启用了此类技术。
  • 灵活性的提高带来了额外的责任。
  • 缺少块结构的锁定可以消除{{code sync}方法和语句中发生的锁定的自动释放。
  • 在大多数情况下,应使用以下惯用法:
  • Lock l = ...;
  • l.lock();
  • try {
  • // access the resource protected by this lock
  • } finally {
  • l.unlock();
  • 当锁定和解锁发生在不同的范围内时,必须小心以确保通过try-finally或try-catch保护持有锁定时执行的所有代码,以确保在必要时释放锁定。
  • {@code Lock}实现通过提供无阻塞尝试来获取锁({@link #tryLock()}),即通过尝试获取获取锁的尝试,提供了比{@code sync}方法和语句更多的功能。
  • 可以被中断({@link #lockInterruptible},并尝试获取可能超时的锁({@link #tryLock(long,TimeUnit)})。
  • {@code Lock}类还可以提供以下行为和语义:与隐式监视器锁定的方式有很大不同,例如保证顺序,不可重入使用或死锁检测;如果实现提供了这种特殊的语义,则实现必须记录这些语义。
  • 请注意,{@code Lock}实例是正常的对象,它们本身可以用作{@code Synchronized}语句中的目标获取一个{@code Lock}实例的监视器锁与调用该实例的任何{@link #lock}方法没有指定的关系。
  • 建议避免conf除非您在自己的实现中使用,否则您永远不要以这种方式使用{@code Lock}实例。
  • 除非另有说明,否则为任何参数传递{@code null}值都会导致抛出{@link NullPointerException}。
  • 内存同步所有{@code Lock}实现必须强制执行与内置监视器锁所提供的相同的内存同步语义,如<a href =“ docs.oracle.com/javase/spec… /html /jls-17.html#jls-17.4“> Java语言规范(17.4内存模型):成功的{@code lock}操作具有与成功的Lock操作相同的内存同步效果。
  • 成功的{@code unlock}操作具有与成功的Unlock操作相同的内存同步效果。
  • 不成功的锁定和解锁操作以及可重入的锁定/解锁操作不需要任何内存同步效果。
  • 实现注意事项锁获取的三种形式(可中断,不可中断和定时)可能在性能特征,订购保证或其他实现质量方面有所不同。
  • 此外,在给定的{@code Lock}类中,可能无法提供中断正在进行的锁获取的功能。
  • 因此,不需要为所有三种形式的锁获取定义完全相同的保证或语义的实现,也不需要支持正在进行的锁获取的中断的实现。
  • 需要一个实现来清楚地记录每个锁定方法提供的语义和保证。
  • 在支持锁获取中断的范围内,它还必须服从该接口中定义的中断语义:全部或仅在方法输入时才这样做。
  • 由于中断通常意味着取消,并且通常不经常进行中断检查,因此与正常方法返回相比,实现可能更喜欢响应中断。
  • 即使可以证明在另一个操作取消线程之后发生中断也是如此。
  • 实现应记录此行为。

源码

package java.util.concurrent.locks;
public interface Lock {

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

    /**
     * 除非当前线程为{@linkplain Thread#interrupt interrupted},否则获取锁。
     * 获取锁(如果有)并立即返回。
     * 如果该锁不可用,则出于线程调度目的,当前线程将被禁用,并且在发生以下两种情况之一之前,它处于休眠状态:该锁由当前线程获取;或当前线程其他某个线程{@linkplain Thread#interrupt interrupts},并且支持中断获取锁。
     * 如果当前线程:在进入此方法时设置了其中断状态;或在获取锁时被{@linkplain Thread#interrupt interrupted中断,并且支持中断获取锁,则抛出{@link InterruptedException}并清除当前线程的中断状态。
     * 实现注意事项在某些实现中,可能无法中断锁获取,并且如果可能的话,这可能是昂贵的操作。
     * 程序员应意识到可能是这种情况。
     * 在这种情况下,实现应记录在案。
     * 与正常方法返回相比,实现可能更喜欢对中断做出响应。
     *  {@code Lock}实现可能能够检测对锁的错误使用,例如可能导致死锁的调用,并且在这种情况下可能引发(未经检查的)异常。
     * 该{@code Lock}实现必须记录情况和异常类型。
     */
    void lockInterruptibly() throws InterruptedException;

    /**
     * 仅在调用时释放锁时才获取锁。
     * 获取锁(如果有),并立即返回值{@code true}。
     * 如果锁不可用,则此方法将立即返回值{@code false}。
     */
    boolean tryLock();

    /**
     * 如果锁在给定的等待时间内是空闲的,并且当前线程尚未{@linkplain Thread#interrupt interrupted},则获取该锁。
     * 如果锁可用,则此方法立即返回值{@code true}。
     * 如果该锁不可用,则出于线程调度目的,当前线程将被禁用,并处于休眠状态,直到发生以下三种情况之一为止:该锁由当前线程获取;或当前线程的其他某个线程{@linkplain Thread#interrupt interrupts},并且支持中断获取锁;或指定的等待时间已过如果获得了锁,则返回值{@code true}。
     * 如果当前线程:在进入此方法时设置了其中断状态;或在获取锁时被{@linkplain Thread#interrupt interrupted中断,并且支持中断获取锁,则抛出{@link InterruptedException}并清除当前线程的中断状态。
     * 如果经过了指定的等待时间,则返回值{@code false}。
     * 如果时间小于或等于零,则该方法将根本不等待。
     * 实现注意事项在某些实现中,可能无法中断锁获取,并且如果可能的话,这可能是昂贵的操作。
     * 程序员应意识到可能是这种情况。
     * 在这种情况下,实现应记录在案。
     * 与正常方法返回或报告超时相比,实现可能更喜欢对中断做出响应。
     *  {@code Lock}实现可能能够检测对锁的错误使用,例如可能导致死锁的调用,并且在这种情况下可能引发(未经检查的)异常。
     * 该{@code Lock}实现必须记录情况和异常类型。
     */
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

    /**
     * 释放锁。
     * 实现注意事项{@code Lock}实现通常会对哪个线程可以释放锁施加限制(通常只有锁的持有者才能释放该锁),如果违反该限制,则可能引发(未经检查的)异常。
     * 该{@code Lock}实现必须记录任何限制和异常类型。
     */
    void unlock();

    /**
     * 返回绑定到此{@code Lock}实例的新{@link Condition}实例。
     * 在等待该条件之前,该锁必须由当前线程持有。
     * 对{@link Condition#await()}的调用将在等待之前自动释放该锁,并在等待返回之前重新获取该锁。
     * 实现注意事项{@link Condition}实例的确切操作取决于{@code Lock}实现,并且必须由该实现记录。
     */
    Condition newCondition();
}