prefácio
Os três artigos anteriores introduziram ReentrantLock
o princípio de bloqueio e liberação de bloqueios. Este artigo usa o código-fonte para entender ReentrantLock
como alguns dos recursos a seguir são implementados, incluindo principalmente os três pontos a seguir:
- reentrante
- Pode ser interrompido
- bloqueio justo
reentrante
// NonFairSync
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
复制代码
Na verdade, ao introduzir o processo de bloqueio, o processo reentrante já foi mencionado, e essa parte do código está no método da ReentrantLock
classe interna .Sync
nonfairTryAcquire
Preste atenção no enunciado aqui else if
. Se state
isso não 0
significa que uma thread foi bloqueada, julga-se aqui que se a thread atual for a thread que está segurando o bloqueio, adicione o state
valor 1
, ou seja, a thread atual adquire o bloqueio após com sucesso adquirindo o bloqueio. Isso é reutilizável. O significado de entrarstate
é adicioná-lo toda vez que você entrar novamente 1
. Correspondentemente, quando o bloqueio é liberado, o número de reentradas precisa ser reduzido várias vezes até que state
o 1
bloqueio seja liberado.state
0
Pode ser interrompido
// ReentrantLock类
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
// AQS类
public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}
// AQS类
private void doAcquireInterruptibly(int arg) throws InterruptedException {
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
复制代码
A chamada interrompível é ReentrantLock
yes lockInterruptibly
, o método que será chamado eventualmente AQS
, doAcquireInterruptibly
o código fonte é como acima.
existirCódigo-fonte ReentrantLock de programação simultânea Java (2)Este artigo analisou o processo de aquisição de bloqueios. Através da introdução anterior, aprendemos que quando uma thread falha em adquirir um bloqueio, ela chamará um método para bloquear. Neste momento, se o método da park
thread for chamado , a execução será interrompido, e o método será bloqueado.O retorno é that , então o código que lança a exceção será executado em resposta à interrupção .interrupt
park
parkAndCheckInterrupt
Thread.interrupted()
true
bloqueio justo
// ReentrantLock#Sync类
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
复制代码
上面是非公平锁获取锁的代码,如果state
等于0
也就是此刻是无锁状态,当前线程就直接尝试CAS
修改state
来获取锁,而阻塞队列是否有等待的线程这里并没有考虑,这也就是非公平的体现。
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
复制代码
这是公平锁获取锁的代码,我们看到如果state
等于0
,这里多了一层判断hasQueuedPredecessors
,来看下源码
public final boolean hasQueuedPredecessors() {
Node t = tail;
Node h = head;
Node s;
return h != t && ((s = h.next) == null || s.thread != Thread.currentThread());
}
复制代码
h!=t
:表示头节点和尾节点指向的不是同一个节点,也就是队列中有其他节点;(s = h.next) == null
:表示head
的后继节点是null
,有可能是头节点还没有指向下一个节点,个人理解参考下面代码,通过CAS设
置了tail
节点,但是head
节点还没有把next
指向下一个节点。
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
复制代码
s.thread != Thread.currentThread()
:表示head
节点的后继节点不是当前线程的节点。 所以如果队列中有节点,并且第二个节点是null
或者不是当前线程的节点,那么当前线程就不能去获取锁以示公平。