在《Java多线程编程核心技术》有这样一道题,同时也是某公司(大华)的一道笔试题目。
文章目录
1. 题目描述2. 思路分析2.1 为什么会想到用CountDownLatch2.2 CountDownLatch应用场景2.3 为什么不用CyclicBarrier / Semaphore
3.代码实现
1. 题目描述
假如有Thread1、Thread2、Thread3、Thread4四条线程分别统计C、D、E、F四个盘的大小,所有线程都统计完毕交给Thread5线程去做汇总,应当如何实现?
DiskMemory类如下
public class DiskMemory {
private int totalSize
;
public int getSize() {
return (new Random().nextInt(3) + 1) * 100;
}
public synchronized void setSize(int size
) {
totalSize
+= size
;
}
}
根据给出的代码,补全剩余代码,实现该功能。
2. 思路分析
这主要考察的是线程的通信机制,多个线程共同协作完成任务
【第一种方法】
直接用join把线程5加入进去即可
【第二种方法】
用juc包下提供的辅助工具类解决
用CountDownLatch : 一个线程(或者多个), 等待另外N个线程完成某个事情之后才能执行
2.1 为什么会想到用CountDownLatch
【分析关键字】
等待:线程5等待前四条线程完成任务后,才开始执行执行一次:每个线程任务只执行一次,共统计四次,四次结束后统计线程停止执行;累加线程开始执行
CountDownLatch 是计数器,一个线程完成自己的线程任务,计数器就会减一(原子操作)。直到计数器减为0时,被阻塞的线程被唤醒执行。
countDown:原子类操作,计数器减一 await:调用该方法的线程被阻塞,直到计数器为0时被唤醒执行
CountDownLatch类是一个同步倒数计数器,构造时传入int参数,该参数就是计数器的初始值,每调用一次countDown()方法,计数器减1,计数器大于0 时, await()方法会阻塞后面程序执行,直到计数器为0,后面被阻塞的方法才会得以实行。await(long timeout, TimeUnitunit),是等待一定时间,然后执行,不管计数器是否到0了。
2.2 CountDownLatch应用场景
在某些业务场景中,程序执行需要等待某个条件完成后才能继续执行后续的操作
【多任务下并行计算】:当某个处理的运算量很大时,可以将该运算任务拆分成多个子任务,等待所有的子任务都完成之后,父任务再拿到所有子任务的运算结果进行汇总
2.3 为什么不用CyclicBarrier / Semaphore
CyclicBarrier :同样是计数器,但是此计数器可被重置。CountDownLatch不可被重置。CyclicBarrier 更实用于多个线程之间的等待,直到所有线程满足条件后,才执行后续操作。类比王者荣耀或者CS等游戏,所有玩家进入到游戏房间后游戏才开始。
Semaphore :是信号量,它主要是用来限流。用来控制访问共享资源的线程数,控制同一时间并发线程的数目。比如说数据库连接池,资源数与线程数相同,一个线程只能获得一个连接,只能获取到一份连接,并且连接数有上限。
3.代码实现
import java
.util
.*
;
import java
.util
.concurrent
.CountDownLatch
;
import java
.util
.concurrent
.ExecutorService
;
import java
.util
.concurrent
.Executors
;
public class DiskMemory {
private int totalSize
;
public int getSize() {
return (new Random().nextInt(3) + 1) * 100;
}
public synchronized void setSize(int size
) {
totalSize
+= size
;
}
public int getTotalSize() {
return totalSize
;
}
public static void main(String
[] args
) throws InterruptedException
{
ExecutorService service
= Executors
.newFixedThreadPool(4);
CountDownLatch countDownLatch
= new CountDownLatch(4);
DiskMemory diskMemory
= new DiskMemory();
for (int i
= 0; i
< 4; i
++) {
service
.execute(() -> {
int timer
= new Random().nextInt(5);
try {
Thread
.sleep(timer
* 1000);
} catch (InterruptedException e
) {
e
.printStackTrace();
}
int diskSize
= diskMemory
.getSize();
System
.out
.printf("完成磁盘的统计任务,耗费%d秒. 磁盘大小为%d.\n", timer
, diskSize
);
diskMemory
.setSize(diskSize
);
countDownLatch
.countDown();
System
.out
.println("count num = " + countDownLatch
.getCount());
});
}
countDownLatch
.await();
System
.out
.printf("全部磁盘都统计完成,所有磁盘总大小为 " + ", 【 totalSize = " + diskMemory
.getTotalSize() + " 】");
service
.shutdown();
}
}
执行结果: