1.为什么要用线程池?
创建线程和线程销毁的开销比较大,占用的时间恐怕要比我们正常业务的时间还要大。而且业务需要可能会频繁的进行创建和销毁,再加上我们本身的业务线程,可能会导致系统资源不足,这种不可控的开销是我们无法接受的。所以我们可以简化这个过程。假如在业务开始之前就维护一组线程,用的时候直接用岂不是很快?
2.线程池的作用
提高效率:如上面说的,业务开始之前我们就维护一个线程池,拿来即用,就像你考试,你做好准备了再考究很轻松效率很高,但是你要临时抱佛脚,那就捉襟见肘了(闲话)
方便管理:上面说过,频繁的进行线程创建和销毁,这样做是不可控的。高并发的情况下,来了一大批请求,难道你要创建一大堆线程吗?分分钟卡死。有了线程池就不一样了,你比如维护了100个线程,现在来了101个请求,你先用100个线程去处理100个请求,把多出来的请求,放进一个等待队列里排队。
3.线程池的使用场景
ThreadPoolExecutor构造方法:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
ThreadPoolExecutor参数:
- corePoolSize: 线程池核心线程数
- maximumPoolSize:线程池最大数
- keepAliveTime: 空闲线程存活时间
- unit: 时间单位
- workQueue: 线程池所使用的缓冲队列
- threadFactory:线程池创建线程使用的工厂
- handler: 线程池对拒绝任务的处理策略
java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService
比较重要的几个类:
ExecutorService | 真正的线程池接口 |
ScheduledExecutorService | 能和Timer/TimerTask类似,解决那些需要任务重复执行的问题 |
ThreadPoolExecutor | ExecutorService的默认实现 |
ScheduledThreadPoolExecutor | 继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现 |
1、newSingleThreadExecutor
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
看下代码:
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int index = i;
singleThreadExecutor.execute(new Runnable() {
@Override
public void run() {
try {
System.out.println(index);
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
2、newFixedThreadPool
创建一个定长的线程池,控制最大的并发线程数,多余的任务放在队列里排队等候
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
final int index = i;
fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
System.out.println(index);
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
3、newCachedThreadPool
创建一个可缓存线程池,如果线程池长度超过请求数,回收空闲的线程。若无可回收,则新建线程。示例代码如下
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int index = i;
try {
Thread.sleep(index * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
cachedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(index);
}
});
}
4.newScheduledThreadPoo
创建一个线程池,定时周期性的执行
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
scheduledThreadPool.schedule(new Runnable() {
@Override
public void run() {
System.out.println("delay 3 seconds");
}
}, 3, TimeUnit.SECONDS);
//表示延迟3秒执行
//例子
scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("delay 1 seconds, and excute every 3 seconds");
}
}, 1, 3, TimeUnit.SECONDS);
参考一篇文章:
4.线程池工作队列
ArrayBlockingQueue | 是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序 |
LinkedBlockingQueue | 一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列 |
SynchronousQueue | 一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列 |
PriorityBlockingQueue | 一个具有优先级的无限阻塞队列 |