Código-fonte ReentrantLock de programação simultânea Java (4)

prefácio

Os três artigos anteriores introduziram ReentrantLocko princípio de bloqueio e liberação de bloqueios. Este artigo usa o código-fonte para entender ReentrantLockcomo 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 ReentrantLockclasse interna .SyncnonfairTryAcquire

Preste atenção no enunciado aqui else if. Se stateisso não 0significa que uma thread foi bloqueada, julga-se aqui que se a thread atual for a thread que está segurando o bloqueio, adicione o statevalor 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 stateo 1bloqueio seja liberado.state0

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 é ReentrantLockyes lockInterruptibly, o método que será chamado eventualmente AQS, doAcquireInterruptiblyo 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 parkthread 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 .interruptparkparkAndCheckInterruptThread.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或者不是当前线程的节点,那么当前线程就不能去获取锁以示公平

おすすめ

転載: juejin.im/post/7229310860732268601