【J.U.C】lockInterruptibly与lock

文章首发于:clawhub.club


在看LinkedBlockingQueue源码时,发现一点疑惑:
poll(long timeout, TimeUnit unit)方法中通过takeLock.lockInterruptibly()获取锁,
其重载方法poll()中,通过takeLock.lock()获取锁。

通过查看ReentrantLock源码中lockInterruptibly()与lock()方法,解决上述问题。

lock()

lock()调用了抽象类Sync(实现了AQS)的lock()方法,这是一个抽象方法,有两个实现,一个公平锁,一个非公平锁,最终都调用到acquire(int arg)方法,内部处理了中断。
优先考虑获取锁,待获取锁成功后,才响应中断。

 /**
     * 获得锁。
     *
     * 如果锁不被其他线程持有,则获取锁,并立即返回,将锁持有计数设置为1。
     *
     * 如果当前线程已经持有锁,那么持有计数将增加1,方法立即返回。
     *
     * 如果锁由另一个线程持有,则当前线程将出于线程调度目的而禁用,
     * 并处于休眠状态,直到获得锁,此时锁持有计数被设置为1。
     */
    public void lock() {
        sync.lock();
    }
    /**
      * 执行锁。子类化的主要原因是允许非公平版本的快速路径。
    */
    abstract void lock();
    /**
     * 以独占模式获取,忽略中断。通过至少调用一次{@link #tryAcquire}来实现,成功后返回。
     * 否则,线程将排队,可能会反复阻塞和解除阻塞,调用{@link #tryAcquire}直到成功。
     * 此方法可用于实现方法{@link Lock# Lock}。
     *
     * @param arg the acquire argument.  This value is conveyed to
     *        {@link #tryAcquire} but is otherwise uninterpreted and
     *        can represent anything you like.
     */
    public final void acquire(int arg) {
        //tryAcquire(arg) 试图以独占模式获取。这个方法应该查询对象的状态是否允许以独占模式获取它,
        //如果允许,也应该查询是否允许以独占模式获取它。
        
        //acquireQueued 获取队列中已存在线程的独占不可中断模式。用于条件等待方法以及获取。
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            //中断当前线程。
            selfInterrupt();
    }

lockInterruptibly()

每个线程存在interrupted status,当线程在sleep或wait,join,此时如果别的线程调用此线程的 interrupt()方法,此线程会被唤醒并被要求处理InterruptedException;
如果此线程在运行中,则不会收到提醒。但是此线程的 interrupted status 会被设置,可以通过isInterrupted()查看并作出处理。
lockInterruptibly()方法调用了isInterrupted()方法,如果发现已经被中断,则抛出InterruptedException。

lockInterruptibly()优先考虑响应中断,再去获取锁。

/**
     * 获取锁,除非线程被打断。
     * 如果锁不被其他线程持有,则获取锁,并立即返回,将锁持有计数设置为1。
     * 如果当前线程已经持有此锁,那么持有计数将增加1,方法立即返回。
     * 如果锁被另一个线程持有,那么当前线程将出于线程调度的目的被禁用,
     * 并处于休眠状态,直到发生以下两种情况之一:
     * 1.锁由当前线程获取。
     * 2.其他线程打断当前线程。
     * 如果锁被当前线程获取,那么锁持有计数被设置为1。
     *
     * 如果当前线程:进入此方法时,其中断状态是否已设置
     * 或者当获取锁时被打断
     * 则会抛出InterruptedException,并清除interrupted status。
     *
     * 在这个实现中,由于这个方法是一个显式的中断点,
     * 所以优先响应中断而不是正常的或可重入的锁获取。
     *
     * @throws InterruptedException if the current thread is interrupted
     */
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }
    /**
     * 以独占模式获取,如果中断将中止。
     * 首先检查中断状态,然后至少调用一次{@link #tryAcquire},成功后返回。
     * 否则,线程将排队,可能反复阻塞和解除阻塞,调用{@link #tryAcquire},直到成功或线程被中断。
     * @param arg the acquire argument.  This value is conveyed to
     *        {@link #tryAcquire} but is otherwise uninterpreted and
     *        can represent anything you like.
     * @throws InterruptedException if the current thread is interrupted
     */
    public final void acquireInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (!tryAcquire(arg))
            doAcquireInterruptibly(arg);
    }

poll(long timeout, TimeUnit unit)方法设计成了多少时间没有获取到锁后,返回,优先相应中断比较合适,
poll()设计成了非阻塞的方法,获取不到直接返回。

你可能感兴趣的:(【J.U.C】lockInterruptibly与lock)