你的关注意义重大!
前言
我们知道使用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() + ":这是最后一个线程!");
}}
运行结果: