Profundo entendimento da programação simultânea - ReentranLock

Este artigo é para ler notas e livros sobre a arte da programação simultânea em Java

1. Bloqueio justo

Justiça é para adquirir bloqueios Se um bloqueio for justo, a ordem de aquisição de bloqueios deve estar em conformidade com a ordem de tempo absoluto da solicitação, ou seja, FIFO.

Ao usar o bloqueio justo, o rastreamento de chamada do método de bloqueio lock () é o seguinte.
1) ReentrantLock: lock ().
2) FairSync: lock ().
3) AbstractQueuedSynchronizer: adquirir (int arg).
4) ReentrantLock: tryAcquire (int adquire).
Na etapa 4, o bloqueio é realmente iniciado.A seguir está o código fonte do método

//java/util/concurrent/locks/AbstractQueuedSynchronizer.java
  public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
//java.util.concurrent.locks.ReentrantLock.FairSync
  protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            //  private volatile int state;  在 AbstractQueuedSynchronizer
            int c = getState();//获取锁的开始,首先读volatile变量state
            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;
        }

解锁:
1) ReentrantLock: unlock ()。
2) AbstractQueuedSynchronizer: release (int arg)。
3) Sync: tryRelease (int releases)。

  protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

Bloqueio injusto

Desde que o estado de sincronização seja definido com sucesso durante a rotação, isso significa que o bloqueio é adquirido
.
1) ReentrantLock: lock ().
2) NonfairSync: lock ().
3) AbstractQueuedSynchronizer: compareAndSetState (int expect, int update).

   final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
            //获取不到锁
                acquire(1);
        }
//AbstractQueuedSynchronizer:compareAndSetState
  protected final boolean compareAndSetState(int expect, int update) {
        // See below for intrinsics setup to support this
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }

Desbloqueie o mesmo bloqueio justo

3. O status do bloqueio não pode ser obtido

  static ReentrantLock reentrantLock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {

        new Thread(() -> {
            m1();
        }, "t1").start();

        Thread.sleep(10);
		//观察t2
        new Thread(() -> {
            m2();
        }, "t2").start();

    }

    public static void m1() {
        reentrantLock.lock();
        try {
            try {
                //睡眠
                Thread.sleep(1000000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } finally {
            reentrantLock.unlock();
        }
    }

    public static void m2() {
        System.out.println("尝试获取锁");
        reentrantLock.lock();
        try {
            System.out.println("获取到锁");
        } finally {
            reentrantLock.unlock();
        }
    }

Insira a descrição da imagem aqui
Sabemos que os bloqueios sincronizados não podem ser adquiridos e o estado desses dois bloqueios é diferente.Por
esse motivo, depende do código fonte

  public final void acquire(int arg) {
  // 再次尝试获取状态,如果还是获取不到就把 当前线程加入队列 
  //返回值是线程是否在自旋的过程中被挂起(park),如果被挂起了,就唤醒
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();//  Thread.currentThread().interrupt();
    }
 final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            //死循环
            for (;;) {
            //	得到前一个 节点
                final Node p = node.predecessor();
                //以下为自旋过程
                // 查看是不是头结点 如果是 就尝试获取锁 获取成功
                if (p == head && tryAcquire(arg)) {
                //把当前节点设置为头结点
                    setHead(node);
                    p.next = null; // help GC
                    //是否失败获取 表明成功获取状态
                    failed = false;
                    //退出自旋,放回线程是否被挂起 (park)
                    return interrupted;
                }
                //以上的条件都没有满足,就把当前线程park
              	
              	// 检查和更新未能获取的节点的状态。
				//如果线程阻塞,返回true。这是主信号
				//控制所有获取循环。需要pred == node.prev。
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
        //结束的时候,如果还是获取不到状态,那就取消这个节点了
            if (failed)
            //取消正在进行的获取尝试。
                cancelAcquire(node);
        }
    }
  private final boolean parkAndCheckInterrupt() {
 		 // 挂起挂起
        LockSupport.park(this);
        //返回当前线程状态 true
        return Thread.interrupted();
    }

Portanto, o encadeamento está no estado de espera, ou seja, o encadeamento é bloqueado durante o processo de rotação;
ou seja, se (shouldParkAfterFailedAcquire (p, node) && parkAndCheckInterrupt ()) esse código-chave

4. Resumo

❑ Quando um bloqueio justo e um bloqueio injusto são liberados, um estado variável volátil deve ser gravado no final.
❑ Ao adquirir um bloqueio justo, ele primeiro lê a variável volátil.
❑ Ao adquirir um bloqueio injusto, a variável volátil é atualizada primeiro com o CAS.Esta operação possui a semântica da memória de leitura e gravação voláteis.

Existem pelo menos duas maneiras de implementar a semântica de memória da aquisição de liberação de bloqueio:
1) Use a semântica de memória de leitura e gravação usando variáveis ​​voláteis.
2) Use a semântica de memória de leitura volátil e gravações voláteis anexadas ao CAS.

Publicado 37 artigos originais · ganhou elogios 6 · vista 4635

Acho que você gosta

Origin blog.csdn.net/littlewhitevg/article/details/105584195
Recomendado
Clasificación