Uso simple e introducción principal de CountDownLatch del sincronizador de subprocesos en el paquete concurrente de Java

usar fondo

Durante el proceso de desarrollo, a menudo se abren varios subprocesos en el subproceso principal para ejecutar tareas en paralelo, y el subproceso principal debe esperar a que todos los subprocesos completen el resumen.

pestillo de cuenta regresiva

Antes de la aparición de CountDownLatch, generalmente se usaba el método join() para implementarlo, pero no era lo suficientemente flexible. Aquí hay un caso de uso:

package concurrentProgramming;

import java.util.concurrent.CountDownLatch;


public class CountDownLatchTest1 {
    
    
    //创建一个CountDownLatch实例,传入的参数为线程数
    private static volatile CountDownLatch countDownLatch = new CountDownLatch(2);

    public static void main(String[] args) throws InterruptedException {
    
    
        Thread threadOne = new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                try {
    
    
                    Thread.sleep(1000);
                    System.out.println("线程1执行完毕!");
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                } finally {
    
    
                    countDownLatch.countDown();
                }
            }
        });

        Thread threadTwo = new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                try {
    
    
                    Thread.sleep(1000);
                    System.out.println("线程2执行完毕!");
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                } finally {
    
    
                    countDownLatch.countDown();
                }
            }
        });

        threadOne.start();
        threadTwo.start();

        System.out.println("等待子线程执行完毕!");

        //等待子线程执行完毕后返回
        countDownLatch.await();

        System.out.println("所有线程执行完毕!");
    }
}

subproceso de gestión de grupos de subprocesos

En el proceso de desarrollo real, debido a que se evita la manipulación directa de los subprocesos, los subprocesos generalmente se administran mediante grupos de subprocesos. Mejore el código anterior para:

package concurrentProgramming;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CountDownLatch2 {
    
    
    private static volatile CountDownLatch countDownLatch = new CountDownLatch(2);
    public static void main(String[] args) throws InterruptedException {
    
    
        //创建线程池管理线程
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        //将线程1添加到线程池中
        executorService.submit(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                try {
    
    
                    Thread.sleep(1000);
                    System.out.println("线程1执行完毕!");
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                } finally {
    
    
                    countDownLatch.countDown();
                }
            }
        });

        //将线程二添加到线程池中
        executorService.submit(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                try {
    
    
                    Thread.sleep(1000);
                    System.out.println("线程2执行完毕!");
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                } finally {
    
    
                    countDownLatch.countDown();
                }
            }
        });

        System.out.println("等待子线程执行完毕!");

        //等待子线程执行完毕后返回
        countDownLatch.await();

        System.out.println("所有线程执行完毕!");
        //关闭线程池
        executorService.shutdown();
    }
}

El resultado de ejecutar los dos fragmentos de código anteriores:

等待子线程执行完毕!
线程2执行完毕!
线程1执行完毕!
所有线程执行完毕!

Exploración de principios

Mirando el código fuente, se puede concluir que CountDownLatch se implementa usando AQS. A través del constructor, encontrará que el valor del contador en realidad se asigna a la variable de estado de AQS, y el estado de AQS se usa para representar el valor del contador.

public class CountDownLatch {
    
    

    private static final class Sync extends AbstractQueuedSynchronizer {
    
    
      
    }
 }

Código fuente del constructor:

    public CountDownLatch(int count) {
    
    
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }

método anular espera ()

Cuando un subproceso llama al método await() del objeto CountDownLatch, el subproceso actual se bloqueará hasta que vuelvan las dos situaciones siguientes: el contador de CountDownLatch es 0; otros subprocesos llaman al método de interrupción del subproceso actual.

    public void await() throws InterruptedException {
    
    
        sync.acquireSharedInterruptibly(1);
    }
    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
    
    
        if (Thread.interrupted())//被中断
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)//计数器已经为0
            doAcquireSharedInterruptibly(arg);
    }

cuenta regresiva vacía ()

Después de que el subproceso llama a este método, el valor del contador disminuye, y si el valor del contador es 0 después de la disminución, todos los subprocesos bloqueados al llamar al método await se activarán.

    public void countDown() {
    
    
        sync.releaseShared(1);
    }

    public final boolean releaseShared(int arg) {
    
    
        if (tryReleaseShared(arg)) {
    
    
            doReleaseShared();
            return true;
        }
        return false;
    }

El método tryReleaseShared en la clase Sync:

 protected boolean tryReleaseShared(int releases) {
    
    
            // Decrement count; signal when transition to zero
            for (;;) {
    
    
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }

Como se puede ver en el código fuente, CAS se usa realmente para asegurar la sincronización del contador al disminuir el valor.

resumen

CountDownLatch es más flexible y cómodo de usar que el método join(). Cuando el subproceso llama al método await, se unirá a la cola de bloqueo de AQS. Solo cuando el contador sea 0, se llamará al método AQS para activar el subproceso bloqueado llamando al método await.

Supongo que te gusta

Origin blog.csdn.net/weixin_42643321/article/details/108506935
Recomendado
Clasificación