L'utilisation de CountDownLatch et l'analyse du code source, l'implémentation de l'écriture manuscrite

L'utilisation et l'analyse du code source de CountDownLatch

CountDownLatch est communément appelé verrou, qui permet à un ou plusieurs threads d'attendre que d'autres threads terminent l'opération spécifiée avant de s'exécuter.

Le constructeur de CountDownLatch accepte un paramètre de type int comme compteur. Si vous voulez attendre l'achèvement de N points, passez N ici.

Lorsque nous appelons la méthode countDown de CountDownLatch, N sera réduit de 1 et la méthode await de CountDownLatch bloquera le thread actuel jusqu'à ce que N devienne zéro.

Étant donné que la méthode countDown peut être utilisée n'importe où, les N points mentionnés ici peuvent être N threads ou N étapes d'exécution dans un thread (un thread peut compter à rebours plusieurs fois). Lorsqu'il est utilisé dans plusieurs threads, transmettez simplement la référence CountDownLatch au thread.

Méthodes dans CountDownLatch

Nom de la méthode La description
CountDownLatch (nombre entier) Construire un CountDownLatch avec un nombre donné
void attendre () Le thread actuel attend jusqu'à ce que le nombre de verrous atteigne zéro, sauf si le thread est interrompu
boolean wait (long timeout, unité TimeUnit) Le thread actuel attend jusqu'à ce que le nombre de verrous atteigne zéro, sauf si le thread est interrompu ou expiré
void countDown () Diminuez le nombre verrouillé, si le nombre atteint zéro, réveillez tous les threads en attente bloqués
long getCount () Renvoyer le décompte actuel

Utilisation de CountDownLatch

S'il y a une telle exigence: nous devons analyser les données de plusieurs feuilles dans un Excel. À ce stade, nous pouvons envisager d'utiliser le multi-threading. Chaque thread analyse les données dans une feuille. Une fois toutes les feuilles analysées, le programme doit indiquer que l'analyse est terminée. .

package com.morris.concurrent.tool.countdownlatch.api;

import lombok.extern.slf4j.Slf4j;

import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * 演示CountDownLatch闭锁的使用
 */
@Slf4j
public class CountDownLatchDemo {
    
    

    private static CountDownLatch countDownLatch = new CountDownLatch(3);

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

        new Thread(()->parse("sheet1"), "t1").start();
        new Thread(()->parse("sheet2"), "t2").start();
        new Thread(()->parse("sheet3"), "t3").start();

        countDownLatch.await();
        log.info("parse commplete");
    }

    private static void parse(String sheet) {
    
    
        log.info("{} parse {} begin...", Thread.currentThread().getName(), sheet);
        try {
    
    
            TimeUnit.SECONDS.sleep(new Random(System.currentTimeMillis()).nextInt(30)); // 随机休眠,模拟解析sheet耗时
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        log.info("{} parse {} end.", Thread.currentThread().getName(), sheet);
        countDownLatch.countDown();
        log.info("还有{}个sheet未解析完", countDownLatch.getCount());
    }
}

Les résultats sont les suivants:

2020-09-24 13:58:55,551  INFO [t2] (CountDownLatchDemo.java:28) - t2 parse sheet2 begin...
2020-09-24 13:58:55,565  INFO [t1] (CountDownLatchDemo.java:28) - t1 parse sheet1 begin...
2020-09-24 13:58:55,597  INFO [t3] (CountDownLatchDemo.java:28) - t3 parse sheet3 begin...
2020-09-24 13:59:01,565  INFO [t1] (CountDownLatchDemo.java:34) - t1 parse sheet1 end.
2020-09-24 13:59:01,566  INFO [t1] (CountDownLatchDemo.java:36) - 还有2个sheet未解析完
2020-09-24 13:59:02,599  INFO [t3] (CountDownLatchDemo.java:34) - t3 parse sheet3 end.
2020-09-24 13:59:02,599  INFO [t3] (CountDownLatchDemo.java:36) - 还有1个sheet未解析完
2020-09-24 13:59:24,556  INFO [t2] (CountDownLatchDemo.java:34) - t2 parse sheet2 end.
2020-09-24 13:59:24,556  INFO [t2] (CountDownLatchDemo.java:36) - 还有0个sheet未解析完
2020-09-24 13:59:24,556  INFO [main] (CountDownLatchDemo.java:24) - parse commplete

Bien sûr, vous pouvez également utiliser join () pour réaliser:

package com.morris.concurrent.tool.countdownlatch.api;

import lombok.extern.slf4j.Slf4j;

import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * 使用join完成对excel的解析,与countDownLatch对比
 */
@Slf4j
public class JoinDemo {
    
    

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

        Thread t1 = new Thread(() -> parse("sheet1"), "t1");
        t1.start();
        Thread t2 = new Thread(() -> parse("sheet2"), "t2");
        t2.start();
        Thread t3 = new Thread(() -> parse("sheet3"), "t3");
        t3.start();

        t1.join();
        t2.join();
        t3.join();
        log.info("parse commplete");
    }

    public static void parse(String sheet) {
    
    
        log.info("{} parse {} begin...", Thread.currentThread().getName(), sheet);
        try {
    
    
            TimeUnit.SECONDS.sleep(new Random(System.currentTimeMillis()).nextInt(30)); // 随机休眠,模拟解析sheet耗时
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        log.info("{} parse {} end.", Thread.currentThread().getName(), sheet);
    }
}

Les résultats sont les suivants:

2020-09-24 14:04:57,732  INFO [t2] (JoinDemo.java:30) - t2 parse sheet2 begin...
2020-09-24 14:04:57,755  INFO [t3] (JoinDemo.java:30) - t3 parse sheet3 begin...
2020-09-24 14:04:57,736  INFO [t1] (JoinDemo.java:30) - t1 parse sheet1 begin...
2020-09-24 14:05:07,757  INFO [t3] (JoinDemo.java:36) - t3 parse sheet3 end.
2020-09-24 14:05:07,758  INFO [t1] (JoinDemo.java:36) - t1 parse sheet1 end.
2020-09-24 14:05:07,757  INFO [t2] (JoinDemo.java:36) - t2 parse sheet2 end.
2020-09-24 14:05:07,759  INFO [main] (JoinDemo.java:26) - parse commplete

Comparaison de CountDownLatch et join:

  • CountDownLatch est plus flexible que join. Join doit récupérer l'objet thread avant de pouvoir être utilisé. En général, le pool de threads est utilisé. Les threads du pool de threads ne sont pas exposés à l'extérieur et la jointure ne peut pas être utilisée.
  • Join doit attendre que le thread se termine avant de revenir, et CountDownLatch.await () peut revenir une fois que le thread est en cours d'exécution à mi-chemin.

Analyse du code source de CountDownLatch

La couche inférieure de CountDownLatch est basée sur AQS.

Structure de données

java.util.concurrent.CountDownLatch.Sync

private static final class Sync extends AbstractQueuedSynchronizer {
    
    

    Sync(int count) {
    
    
        setState(count); // 构造时初始化count的大小
    }

    int getCount() {
    
    
        return getState();
    }

    // await()调用此方法,不为0就会进入同步队列中等待,为0就会直接返回,往下执行
    protected int tryAcquireShared(int acquires) {
    
    
        return (getState() == 0) ? 1 : -1;
    }

    // countDown()调用此方法,每调用一次state就会-1,当state=0时,会去唤醒同步队列中等待的线程
    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;
        }
    }
}

compte à rebours()

java.util.concurrent.CountDownLatch # countDown

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

java.util.concurrent.locks.AbstractQueuedSynchronizer # releaseShared

public final boolean releaseShared(int arg) {
    
    
    if (tryReleaseShared(arg)) {
    
     // state-1
        doReleaseShared(); // 唤醒同步队列中等待的线程
        return true;
    }
    return false;
}

attendre()

java.util.concurrent.CountDownLatch # await ()

public void await() throws InterruptedException {
    
    
    sync.acquireSharedInterruptibly(1);
}

java.util.concurrent.locks.AbstractQueuedSynchronizer # AcquérirSharedInterruptiblement

public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
    
    
    if (Thread.interrupted())
        throw new InterruptedException();
    if (tryAcquireShared(arg) < 0) // 判断state是否为0,是就会直接返回
        doAcquireSharedInterruptibly(arg); // 进入同步队列中等待,park
}

CountDownLatch personnalisé

package com.morris.concurrent.tool.countdownlatch.my;

import java.util.concurrent.TimeUnit;

/**
 * 使用wait-notify实现CountDownLatch
 */
public class WaitNotifyCountDownLatch {
    
    

    private volatile int count;

    public WaitNotifyCountDownLatch(int count) {
    
    
        this.count = count;
    }

    public synchronized void countDown() {
    
    
        if (0 == --count) {
    
    
            this.notifyAll();
        }
    }

    public synchronized void await() throws InterruptedException {
    
    
        while (count > 0) {
    
    
            this.wait();
        }
    }
    
    public synchronized void await(long timeout, TimeUnit unit) throws InterruptedException {
    
    
        while (count > 0) {
    
    
            this.wait(unit.toMillis(timeout));
        }
    }

    public int getCount() {
    
    
        return count;
    }
}

Je suppose que tu aimes

Origine blog.csdn.net/u022812849/article/details/108795637
conseillé
Classement