java并发-线程池
线程池的介绍
Java线程池表示一组等待工作并多次重用的工作线程。在线程池的情况下,创建了一组固定大小的线程。服务提供者从线程池中拉出一个线程并分配一个作业。完成作业后,线程再次包含在线程池中。
使用线程池可以节省多线程应用程序中的资源,同时加快响应速度。线程池还可以指定线程的数量,避免线程过多
对每个任务都开一个线程的情况:
public class OneThread {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(new Task());
thread.start();
}
}
static class Task implements Runnable {
@Override
public void run() {
System.out.println("finished");
}
}
}
/*
* finished
finished
finished
finished
finished
finished
finished
finished
finished
finished
Process finished with exit code 0
* */
这样开销太大,同时过多的线程会占用太多的内存
创建和停止线程池
构造函数参数
corePollSize和maxPollSize
corePollSize:核心线程数,线程池在完成初始化之后,默认情况下线程池中没有任何线程,线程池会等待有任务时,再创建新线程执行任务
maxPollSize:线程池有可能会在核心线程数的基础上,额外增加一些线程,但是新增加的线程有数量上限,就是maxPollSize
添加线程规则
- 如果线程数小于corePoolSize,即使其他工作线程处于空闲状态,也会创建以恶搞新线程来执行
新任务 - 如果线程数大于等于corePoolSize,小于maxmumPoolSize,将任务放入队列(workQueue)
- 如果队列已满,线程数小于maxmumPoolSize,创建一个新线程来执行任务
- 如果队列已满,线程数大于等于maxPollSize,就会拒绝任务
- 如果设置corePollSize和maxPollSize相同,就可以创建固定大小的线程池
- 线程池希望保持较少的线程数,只有在负载变得很大的时候才会增加线程
- 通过设置maxPollSize为很高的值,就可以允许线程池容纳任意数量的并发任务
- 只有队列满的时候才会创建多于corePollSize的线程,如果队列无界,那么线程数就不会超过corePollSize
keepAliveTime
如果线程池当前的线程数多于corePollSize,多余的线程空闲时间超过keepAliveTime就会被终止
ThreadFactory
新的线程是由ThreadFactory创建的,默认创建出来的线程都在一个线程组,优先级为NORM_PRIORITY,不是守护线程
如果是自己指定ThreadFactory,就可以改变线程名、线程组、优先级以及是否是守护线程
workQueue
三种常见的队列:
- 直接交换:SynchronousQueue
- 无界队列:LinkedBlockingQueue
- 有界队列:ArrayBlockingQueue
创建线程池
- newFixedThreadPool
使用LinkedBlockingQueue
传进去的LinkedBlockingQueue没有容量上限。当请求越来越多,并且没有及时处理完毕时,会占用大量内存,可能会导致OOM
- newSingleThreadExcetor
- 使用LinkedBlockingQueue
和newFixedThreadPool的原理基本一致,只是将线程数设置为了1,也会导致和newFixedThreadPool相同的内存问题
- newCachedThreadPool
使用SynchronousQueue
可缓存线程,无界线程池,具有回收多余线程的功能
corePollSize被设置为Integer.MAX_VALUE,这会导致创建非常多的线程,可能会导致OOM
- newScheduledTheadPool
使用DelayedWorkQueue
支持定时、周期性执行任务
线程数量的设定
- CPU密集型(加密、计算hash):最佳线程数为CPU核心数的1-2倍
- 耗时IO型(读写数据库、文件、网络读写):最佳线程数一般大于CPU核心数很多倍,因为CPU的速度大于外设的速度,会产生空闲。要以JVM线程监控繁忙情况为依据
- 线程数=CPU核心数*(1+平均等待时间 / 平均工作时间)
停止线程池
- shutdown
执行之后,会将线程池存在的任务以及队列中等待的任务都执行完毕再停止,同时再有新任务时直接拒绝
- isShutdown
会返回一个布尔值,告诉我们线程池是不是已经停止了(是不是进入停止的状态了而不是完全停止)
- isTerminated
会返回一个布尔值,告诉我们线程池是不是完全停止了
- awaitTermination
这个方法不是停止线程的。是等待一段时间,在这段时间内,如果线程停止了,就返回true,否则,返回false
- shutdownNow
立刻停止线程池。对于正在执行的线程,会进行interrupt;对于还在队列中等待的线程会直接返回
如果不了解interrupt,可以去看我写的这一篇文章:java多线程-线程的停止【interrupt】_java多线程interrupt_健鑫.的博客-CSDN博客
任务的拒绝(学习一下是如何拒绝的,用到生活中 doge)
拒绝的时机
- 当Executor关闭时,提交的新任务会被拒绝
- 当Executor对最大线程和工作队列的容量使用有限边界并饱和时会拒绝
拒绝的策略
- AbortPolicy
抛出异常
- DiscardPolicy
直接把任务仍了,不会通知,所以你不知道任务得没得到处理
- DiscardOldestPolicy
将队列中最老的任务丢弃
- CallerRunsPolicy
谁提交的任务谁跑,让提交任务的线程去执行(老子没空,你牛逼你自己来)