参考资料
https://www.cnblogs.com/dolphin0520/p/3932921.html
https://www.jianshu.com/p/896b8e18501b
简介
Java线程池的核心类为ThreadPoolExecutor
线程池中的FixedThreadPool、SingleThreadExecutor、CachedThreadPool、ScheduledThreadPool底层均是调用了ThreadPoolExecutor的构造方法初始化的线程池
线程池与连接池的原理类似,省去了频繁的初始化和销毁的步骤,节省了时间,维护着活跃的线程供调用。
ThreadPoolExecutor构造方法和基本原理
1、ThreadPoolExecutor构造方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueueworkQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
通过源码可以看到ThreadPoolExecutor核心的构造方法,下面来分析下具体的参数的含义
corePoolSize ---- 保持在池中的线程数,即使他们是空闲的
maximumPoolSize --- 池中允许的最大线程数
keepAliveTime ---- 当线程数大于corePoolSize,多余的空闲线程在终止前等待新任务的最大时间。
unit ---- keepAliveTime 中参数的时间单位
workQueue ---- 任务执行前用于保存任务的队列
threadFactory ---- 执行程序创建新线程时要使用的工厂 (一般默认)
handler ---- 由于达到线程边界和队列容量而阻塞执行时使用的处理程序 (一般默认)
- 1、workQueue
workQueue的类型为BlockingQueue
1)ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小;
2)LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;(默认的就是所谓的无界队列)
3)synchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。
2、基本原理
这个样例我是参考的,感觉说的很好
corePoolSize在很多地方被翻译成核心池大小,其实我的理解这个就是线程池的大小。举个简单的例子:
假如有一个工厂,工厂里面有10个工人,每个工人同时只能做一件任务。
因此只要当10个工人中有工人是空闲的,来了任务就分配给空闲的工人做;
当10个工人都有任务在做时,如果还来了任务,就把任务进行排队等待;
如果说新任务数目增长的速度远远大于工人做任务的速度,那么此时工厂主管可能会想补救措施,比如重新招4个临时工人进来;
然后就将任务也分配给这4个临时工人做;
如果说着14个工人做任务的速度还是不够,此时工厂主管可能就要考虑不再接收新的任务或者抛弃前面的一些任务了。
当这14个工人当中有人空闲时,而新任务增长的速度又比较缓慢,工厂主管可能就考虑辞掉4个临时工了,只保持原来的10个工人,毕竟请额外的工人是要花钱的。
这个例子中的corePoolSize就是10,而maximumPoolSize就是14(10+4)。
也就是说corePoolSize就是线程池大小,maximumPoolSize在我看来是线程池的一种补救措施,即任务量突然过大时的一种补救措施。
示例:
ThreadPoolExecutor pool = new ThreadPoolExecutor(2,5,10L, TimeUnit.SECONDS,new ArrayBlockingQueue<>(5));
如下线程池最多能接受10个线程,等待队列里5个,最大线程数5个,多的线程请求将会被拒绝
1、线程池的关闭
线程池关闭有两种方式 shutdown()和shutdownNow()
具体区别
1、shutdown()
shutdown()会将正在只能执行的任务和等待队列的任务执行完成,但是不接收新的任务
2、shutdownNow()
shutdownNow()会中断所有正在执行的任务,清空等待队列
2、测试代码(大家可以根据这个测试代码自行测试)
public class ShutdownAndShutdownNow {
public static void main(String[] args) throws InterruptedException {
final ExecutorService executor = Executors.newFixedThreadPool(2);
for (int i = 0; i < 10; i++) {
executor.submit(new Ceshi());
}
//这一步的操作是让所有的任务进入等待队列
// Thread.sleep(2000);
//shutdown会将正在只能执行的任务和等待队列的任务执行完成,但是不接收新的任务
executor.shutdown();
//shutdownNow会中断所有正在执行的任务,清空等待队列
// executor.shutdownNow();
System.out.println("hahah");
}
static class Ceshi implements Runnable {
@Override
public void run() {
try {
Thread.sleep(5000);
System.out.println("heiheihei");
} catch (InterruptedException e) {
}
}
}
}
简单代码示例
我们这边有个需求,就是从mysql拉取数据写入到HDFS上,因为mysql支持分页,所以我们这边打算就用线程池来做
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(3, 3, 1,
TimeUnit.SECONDS, new LinkedBlockingQueue(),
new ThreadPoolExecutor.DiscardOldestPolicy());
for (int i = 0; i < 3; i++) {
String sql = "select * from DWA.DWA_DEMO limit " + (i * avg + 1) + "," + (avg);
System.out.println(sql);
threadPool.submit(new MysqlRunable(sql,bw));
}
threadPool.shutdown();
while (!threadPool.isTerminated()) {
}
其中threadPool.isTerminated() 是为了等待所有线程执行完毕
我们这边的workQueue选择的是LinkedBlockingQueue无界队列,因为也考虑到队列各自独立而且必须执行。