Profundo conocimiento de la programación concurrente: ReentranLock

Este artículo es para leer notas y libros sobre el arte de la programación concurrente en Java

1. Cerradura justa

La equidad es para adquirir cerraduras Si una cerradura es justa, el orden de adquisición de cerraduras debe ajustarse al orden de tiempo absoluto de la solicitud, es decir, FIFO.

Cuando se usa el bloqueo justo, el método de bloqueo lock () call trace es el siguiente.
1) ReentrantLock: lock ().
2) FairSync: bloqueo ().
3) AbstractQueuedSynchronizer: adquirir (int arg).
4) ReentrantLock: tryAcquire (int adquire).
En el paso 4, el bloqueo se inicia realmente. A continuación se muestra el código fuente del 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;
        }

Bloqueo injusto

Siempre que el estado de sincronización se establezca correctamente durante el giro, significa que se adquiere el bloqueo
.
1) ReentrantLock: lock ().
2) NonfairSync: bloqueo ().
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);
    }

Desbloquee el mismo candado justo

3. No se puede obtener el estado de la cerradura

  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();
        }
    }

Inserte la descripción de la imagen aquí
Sabemos que no se pueden adquirir bloqueos sincronizados, y el estado de estos dos bloqueos es diferente.
En cuanto a la razón, depende del código fuente

  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();
    }

Entonces, el hilo está en el estado de espera, es decir, el hilo está bloqueado durante el proceso de giro;
es decir, si (shouldParkAfterFailedAcquire (p, node) && parkAndCheckInterrupt ()) este código clave

4. Resumen

❑ Cuando se libera un bloqueo justo y un bloqueo injusto, se debe escribir un estado variable volátil al final.
❑ Al adquirir un bloqueo justo, primero lee la variable volátil.
❑ Cuando se adquiere un bloqueo injusto, la variable volátil se actualiza primero con CAS. Esta operación tiene la semántica de memoria tanto de lectura volátil como de escritura volátil.

Hay al menos dos formas de implementar la semántica de memoria de la adquisición de liberación de bloqueo:
1) Usar la semántica de memoria de escritura-lectura usando variables volátiles.
2) Use la semántica de memoria de lectura volátil y escrituras volátiles adjuntas a CAS.

Publicado 37 artículos originales · ganado elogios 6 · vistas 4635

Supongo que te gusta

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