CountDownLatch讲解-Junit实现多线程测试

前言

我们知道使用junit进行多线程测试时,主线程执行完毕后直接结束所有线程,(这就好比我前文说线程五种状态中的“STOP”,就是不接受新任务,中断当前正在运行的任务。)导致很多子线程没有执行完呢就已经关闭了,所以为了解决这个问题我们就需要讲解一个JUC又一个重要的类CountDownLatch,使用AQS状态表示计数,可以把它看成是一个计数器,源码注释第一句话:A synchronization aid that allows one or more threads to wait until,翻译过来就是:允许一个或多个线程等待。好,让我们揭开CountDownLatch的面纱,并用它解决junit多线程问题。

正文

胖虎带领大家阅读一下CountDownLatch类的部分源码,当然我不会把源码将一个遍,只会说一些重要的内容,不过大家不要慌,CountDownLatch中一共就312行代码,80%是注释,哈哈。

首先CountDownLatch类中官方的解释是:

允许一个或多个线程等待,在其他线程中执行的一组操作完成。用给定的<em>count</em>初始化。方法块{@link#await await}直到当前计数达到由于调用{@link#countDown}方法而为零,之后释放所有等待的线程以及随后的任何调用。

让我们三步走,介绍最重要的三个方法

step1:

该类的构造方法,声明一个CountDownLatch 实例  预定计数次数:5

CountDownLatch countDownLatch = new CountDownLatch(5);


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

step2:

递减锁存器的计数,如果计数到达零,则释放所有等待的线程。  如果当前计数大于零,则将计数减少 1 。

countDownLatch.countDown();

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

step3:

使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断 , 如果当前的计数为零,则此方法立即返回 。

countDownLatch.await();

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

重点的三个方法讲解完毕,其实await()方法还有一个是可以设定等待时常的,我就不贴出来了,是不是觉得很简单。

下面就来让我们使用CountDownLatch在junit多线程环境下保证线程的原子性,说通俗点,就是我创建的所有线程都会执行完毕。

我这个例子是使用的是springboot2.0.2.RELEASE版本自带的junit测试,我这里直接写的是正例,大家拷贝过去可以吧我代码关于countDownLatch对象引用的地方注释掉再运行就是反例,运行结果肯定大于0,因为我五个线程还没运行完就结束了。

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

import org.junit.Test;

import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = AppServiceInterfaceApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class TeseCase {
    private static Integer count = 10000;
    private CountDownLatch countDownLatch = new CountDownLatch(10000);
    private static final Object obj = new Object();

    @Test
    public void test() throws InterruptedException {    
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        for (int i = 1; i <= 10000; i++) {
            try {  
                executorService.execute(() -> {
                    //同步的去做减法
                    synchronized (obj) {
                        count--;
                    }
                });
            } catch (Throwable e) {
                //TODO
            } finally {
                // 很关键, 无论上面程序是否异常必须执行countDown,否则await无法释放
                countDownLatch.countDown();
            }
        }
        // 5个线程countDown()都执行之后才会释放当前线程,程序才能继续往后执行
        countDownLatch.await();
        //关闭线程池
        executorService.shutdown();
        System.out.println(count);
        System.out.println(Thread.currentThread().getName() + ":这是最后一个线程!");
    }
}

运行结果:

猜你喜欢

转载自blog.csdn.net/weixin_38003389/article/details/85096530
今日推荐