引言

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

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

lock()

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
* 获得锁。
*
* 如果锁不被其他线程持有,则获取锁,并立即返回,将锁持有计数设置为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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
* 获取锁,除非线程被打断。
* 如果锁不被其他线程持有,则获取锁,并立即返回,将锁持有计数设置为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()设计成了非阻塞的方法,获取不到直接返回。