Java concurrencia herramientas analíticas -CountDownLatch

concepto básico

Por lo general, desarrollar, debe venir a través de una demanda de este tipo: una función requiere varios hilos juntos en colaboración, y luego tener que esperar a que se procesan estos hilos, y para continuar con la operación de seguimiento. A continuación, podemos optar por utilizar este conjunto de herramientas concurrente CountDownLatch.

uso

package com.demo;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
    	// 参数5表示我们需要等待的线程数
        CountDownLatch cdl = new CountDownLatch(5);
        // 启动5个子线程
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                try {
                    // ...
                    Thread.sleep(3000);
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    // 子线程完成以后,调用CountDownLatch.countDown()方法
                    System.out.println(Thread.currentThread().getName() + "执行完成");
                    cdl.countDown();
                }
            }, "线程-" + (i+1)).start();
        }
        // 调用调用await方法后,主线程阻塞,并等待所有子线程执行完成
        cdl.await();
        System.out.println("子线程全部执行完成:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    }
}
复制代码

resultados de la ejecución son las siguientes:

线程-1执行完成
线程-4执行完成
线程-5执行完成
线程-2执行完成
线程-3执行完成
子线程全部执行完成:2020-03-12 12:42:41
复制代码

Vemos el principal Chengkai Qi cinco sub-hilo, llamado CountDownLatch.await () hilo método de emperador en el estado bloqueado, cuando se haya completado toda la ejecución del sub-hilo, el hilo principal puede continuar la ejecución.

principio interno

Hacemos un seguimiento de la fuente de mirar la parte interna de la CountDownLatch. En primer lugar, en primer lugar CountDownLatch un nuevo objeto en su constructor:

public class CountDownLatch {
    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        // 新建一个同步器
        this.sync = new Sync(count);
    }
}
复制代码

A continuación, crear una nueva objetos de sincronización, Sync es su clase interna, vamos vistazo:

    private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;
        Sync(int count) {
        	// 将同步状态设置为指定的值
            setState(count);
        }
        int getCount() {
            return getState();
        }
        // 省略一些代码
		...
    }
复制代码

Vemos sincronización heredó AQS (AbstractQueuedSynchronizer, descripción CountDownLatch se basa principio de aplicación AQS ) implementado en forma setstate aquí Estado Estado justo sincronización asignado al número de hilos que pase.

public abstract class AbstractQueuedSynchronizer
	...
	// 将同步状态设置为5
    protected final void setState(int newState) {
        state = newState;
    }
}
复制代码

El anterior es un nuevo objeto CountDownLatch lógica, vistazo a continuación esperan ser invocada de lo que se ha hecho en el método esperan:

public class CountDownLatch {
 	public void await() throws InterruptedException {
        // 父类AbstractQueuedSynchronizer中实现
        sync.acquireSharedInterruptibly(1);
    }
}
复制代码

acquireSharedInterruptibly Este método se implementa en la clase padre AbstractQueuedSynchronizer de sincronización:

public abstract class AbstractQueuedSynchronizer
	...
    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
         // 获取共享锁
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }
	// 这边空实现,其它是交给子类去实现,我们这边是Sync
    protected int tryAcquireShared(int arg) {throw new UnsupportedOperationException();
    }
}
复制代码

Volvemos a la sincronización, el aspecto tryAcquireShared implementación de este método:

    protected int tryAcquireShared(int acquires) {
   	   return (getState() == 0) ? 1 : -1;
    }
复制代码

Aquí es simple, relativamente estado de estado de sincronización, igual a 0 devuelve 1, anti - 1 se devuelve desde el principio, que se convirtió en 5 inicialización, asi que aquí devuelve -1. Respaldar a AbstractQueuedSynchronizer en:

    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        // 这边返回确实小于0
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }
复制代码

Debido a que la parte delantera tryAcquireShared de retorno es -1, la condición se cumple si el lado, en el método doAcquireSharedInterruptibly:

public abstract class AbstractQueuedSynchronizer
	...
    private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        // 当前线程包装成node放入阻塞队列
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null;
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
}
复制代码

Aquí sólo se preocupa método parkAndCheckInterrupt:

 private final boolean parkAndCheckInterrupt() {
 		// 阻塞当前线程
        LockSupport.park(this);
        return Thread.interrupted();
    }
复制代码

Análisis Hasta ahora, sabemos que el hilo principal llama método de ejecución en lo esperan habría sido hasta aquí. Entonces, ¿cómo es que sea lo despertó? Luego continuamos seguirlo para lograr CountDownLatch.countDown, entramos en este enfoque:

public class CountDownLatch {
	...
    public void countDown() {
    	// 调用父类AbstractQueuedSynchronizer的方法
        sync.releaseShared(1);
    }
}
复制代码

También releaseShared Este método se implementa en la clase padre en AbstractQueuedSynchronizer:

public abstract class AbstractQueuedSynchronizer
    public final boolean releaseShared(int arg) {
    	// 模板方法,调用子类Sync的tryReleaseShared方法
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }
}
复制代码

tryReleaseShared se implementa en Sincronizar en:

protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();
                // 同步状态已经是0了就直接返回false
                if (c == 0)
                    return false;
                int nextc = c-1;
                // 使用CAS操作:同步状态减1
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }
复制代码

La lógica de este código es que cada vez que se llama una vez que el método de cuenta atrás, el estado de estado de sincronización restará 1, devolverá verdadero si el estado de estado de sincronización va a cero, por lo que cuando terminamos el último sub-hilo método de ejecución que cuenta regresiva se devolverá true. Volvemos a métodos releaseShared:

public abstract class AbstractQueuedSynchronizer
   public final boolean releaseShared(int arg) {
   		// 当最后一个线程执行完,tryReleaseShared返回true
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }
 }
复制代码

Sabemos que la última ejecución del hilo se ha completado, tryReleaseShared devuelve verdadero, entrará método doReleaseShared:

   private void doReleaseShared() {
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;  
                    // 唤醒阻塞的线程
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;
            }
            if (h == head)
                break;
        }
    }
复制代码

Aquí sólo se preocupa método unparkSuccessor:

    private void unparkSuccessor(Node node) {
        ...// 省略一些代码
        if (s != null)
        	// 唤醒线程
            LockSupport.unpark(s.thread);
    }
复制代码

En última instancia, vemos que cuando se terminó el último hilo hijo, el hilo principal se despertó.

Supongo que te gusta

Origin juejin.im/post/5e69d597e51d452717263f8f
Recomendado
Clasificación