池化是在为了复用常用的一种技术解决思路,比如连接池、线程池等,线程池主要是为了降低线程创建和销毁造成的资源消耗,进而起到解决系统资源,提高系统相应速度的目的。Java中的ThreadPoolExecutor是JDK为我们提供好的线程池工具。
ThreadPoolExecutor executor = new ThreadPoolExecutor(50,//核心池大小
5000,//最大池大小
200,//线程最大空闲时间,超过此空闲时间可以被收回
TimeUnit.MILLISECONDS, //最大空闲时间的单位
new ArrayBlockingQueue(10)//用于保存执行任务的队列,10的意思是可以允许10个任务在排队,如果队列满了,则创建新的线程。
);
向线程池中提交任务
Task myTask = new Task();
executor.execute(myTask);
//任务定义
class Task implements Runnable {
.....
}
executor.submit(myTask)
也可以用于提交任务,但是主要用于带返回值的Task,用的并不多,因此不做赘述。
在此篇博文中我们用CountDownLatch来记录任务的完成情况,完成线程间的通信。
使用了多线程处理任务后,最常见的需要就是主线程并不知道任务的完成情况,因此无法决定是继续往下进行还是继续完毕。CountDownLatch是JDK提供给我们的多线程间通信的一个工具,用于让主线程知道任务完成的进度。
举个例子来说:
目前一名老师接到了一个任务,说要把教室里的桌子都擦干净
流程是:
如果不用多线程做的话,教室里有1000张桌子,老师(主线程)要逐一把桌子擦干净,然后去敲上课铃。效率比较低,于是老师叫来了50个同学(50个线程),告诉他们要擦桌子。但是问题是老师无法检查每张桌子是否都擦了,所以不知道什么时候去敲上课铃,于是约定同学擦完一张桌子就要举手报告老师一次,老师只需要记录同学的举手次数,等到同学的举手次数到了1000次,老师去敲上课铃。而这个CountDownLatch其实就是这个计数器
主线程的代码:
//声明,1000的意思,意味着1000个任务等待完成
CountDownLatch n = new CountDownLatch(1000);
//将1000个任务交给线程池去处理
for(int i=0;i<1000;i++){
//启动一个任务
Task myTask = new Task(i,n);
executor.execute(myTask);
}
//等待所有线程完毕
try {
n.await();
} catch (InterruptedException e) {
logger.error(e);
}
//去敲上课铃
任务的代码:
public void run() {
//做你的事情,擦桌子
//....
//事情干完了
n.countDown();
}
当然,除了CountDownLatch,多线程通信中还存在CyclicBarrier和Semaphore等,但是CountDownLatch是其中最常用也最简单的一种方式,其他的方式,大家自行搜索理解即可。
最后附上我经常用的做简单的多线程测试时,模拟高并发时的代码,希望能节约大家一点时间。
Tester.java
package com.test;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import java.util.concurrent.*;
public class Tester {
private static Logger logger = LogManager.getLogger(Tester.class);
public void doSome(int num){
//启用线程池(最大不能超过500个)
ThreadPoolExecutor executor = new ThreadPoolExecutor(50,//核心池大小
5000,//最大池大小
200,//线程最大空闲时间,超过此空闲时间可以被收回
TimeUnit.MILLISECONDS, //最大空闲时间的单位
new ArrayBlockingQueue(10)//用于保存执行任务的队列,可以选择其他不同的队列来做任务管理
);
CountDownLatch n = new CountDownLatch(num);
for(int i=0;i//启动一个任务
Task myTask = new Task(i,n);
executor.execute(myTask);
}
logger.info("全部执行的任务数量:"+executor.getTaskCount());
logger.info("已完成的任务数量:"+executor.getCompletedTaskCount());
logger.info("曾经创建过最大的线程数量:"+executor.getPoolSize());
logger.info("活动的线程数量:"+executor.getPoolSize());
//等待所有线程完毕
try {
n.await();
} catch (InterruptedException e) {
logger.error(e);
}
//关闭线程池
executor.shutdown();
}
class Task implements Runnable {
private int taskNum;
CountDownLatch n;
public Task(int num,CountDownLatch n) {
this.taskNum = num;
this.n= n;
}
public void run() {
try{
//TODO 做你的事情,擦桌子
}catch(Exception e){
logger.error(e);
logger.info("task "+taskNum+"执行失败");
}
//事情干完了
n.countDown();
}
}
}