前言
之前语音资源部需要下载一批音频,当时开发一个多线程下载工具,其中多线程处理逻辑,就是将一批任务分配给多个线程去处理,其实代码简化下来就是用多线程处理一个list。
简化代码
public class HandleThread extends Thread {
private static final Log LOGGER = LogFactory.getLog(HandleThread.class);
private String threadName;
private List<String> data;
private int start;
private int end;
private CountDownLatch CountDownLatch;
public HandleThread(String threadName, List<String> data, int start, int end,
CountDownLatch countDownLatch) {
this.threadName = threadName;
this.data = data;
this.start = start;
this.end = end;
this.countDownLatch=countDownLatch;
}
public void run() {
List<String> subList = data.subList(start, end);
LOGGER.info(threadName + "处理了" + subList.size() + "条!");
//具体的下载逻辑
down(subList);
//当前线程处理完之后计数器减一
countDownLatch.countDown()
}
}
具体的任务分配逻辑
/**
* 一批任务,分配n个线程去处理
* @param data
* @param threadNum
*/
public synchronized void handleList(List<String> data, int threadNum) {
if (CollectionUtils.isEmpty(data)) {
return;
}
//初始化一个计数器
CountDownLatch countDownLatch = new CountDownLatch(threadNum);
int length = data.size();
int tl = length % threadNum == 0 ? length / threadNum : (length
/ threadNum + 1);
for (int i = 0; i < threadNum; i++) {
int end = (i + 1) * tl;
HandleThread thread = new HandleThread("线程[" + (i + 1) + "] ",
data, i * tl, end > length ? length : end, countDownLatch);
thread.start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
LOGGER.info("所有音频下载完成");
}
CountDownLatch和join的区别
- countDownLatch 原理:可以理解为一个计数器。在初始化 CountDownLatch 的时候会在类的内部初始化一个int的变量,每当调用 countDownt() 方法的时候这个变量的值减1,而 await() 方法就是去判断这个变量的值是否为0,是则表示所有的操作都已经完成,否则继续等待。
- join 原理:在当前线程中调用另一个线程线程 thread 的 join() 方法时,会调用该 thread 的 wait() 方法,直到这个 thread 执行完毕(JVM在 run() 方法执行完后调用 exit() 方法,而 exit() 方法里调用了 notifyAll() 方法)会调用 notifyAll() 方法主动唤醒当前线程。
- join和countDownLatch都能实现让当前线程阻塞等待其他线程执行完毕,join使用起来更简便,不过countDownLatch粒度更细。不需要一定将线程里面所有任务都完成之后再进行计数器减一,我们可以细粒度的控制到任务的环节。