new thread弊端
-
线程的每次创建和销毁性能差
-
线程缺乏管理,可能造成无限制的创建,造成宕机或OOM
线程池好处
-
重用已经存在的线程
-
提高系统资源利用率
ThreadPoolExecutor
构造参数
- corePoolSize:核心线程数量
- maximumPoolSize:最大线程数量
- keepAliveTime:线程(超出核心线程数的线程)的最大空闲时间
- unit:最大空闲时间单位
- workQueue:阻塞队列,存储等待执行的任务 java队列
- threadFactory:线程工厂,用于创建线程
- rejectHandler:当任务队列满时,所执行的拒绝策略
- Runnable 中的run函数,即封装线程所执行的任务
- ThreadPoolExecutor默认使用AbortPolicy拒绝策略,直接抛出一个RejectedExecutionException异常 拒绝策略
corePoolSize、maximumPoolSize、workQueue三者关系
- 池中线程数小于corePoolSize,无论是否有空闲线程,都会新建线程去执行任务
- 池中活跃线程大于corePoolSize,新任务进入队列,等待空闲线程执行
- 队列满,池中线程数小于maximumPoolSize,对新任务新建线程去执行
- 队列满,池中线程等于maximumPoolSize,对新任务执行拒绝策略
线程池中的函数
- execute():提交任务交给线程池执行
- submit():提交任务,可以返回执行结果 execute+future
- shutdown():等待任务都执行完,关闭线程池
- shutdownNow():不等待任务执行完,关闭线程池
- getTaskCount():返回已安排执行的大致任务总数(任务和状态在计算中有可能改变)
- getCompletedTaskCount():返回已经完成的大致任务总数(任务和状态在计算中有可能改变)
- getPoolSize():线程池中的线程数量
- getActiveCount():正在执行任务的大致线程数量
使用Executors工具类创建的线程池
- newCachedThreadPool()
/**
*创建一个根据需要创建新线程的线程池,当线程可用时将重用以前创建的线程,适用于执行许多短期的异步
*任务,如果没有可用线程将创建新线程放到池子中,超过60s空闲时间将销毁线程,因此长时间空闲的线
*程池不会消耗资源
*
*/
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
- newFixedThreadPool(int nThreads)
/**
*创建一个固定数量的线程池,使用无界队列,当线程都处于活跃状态时,任务将加入到无界队列里面等待
*有可用的线程,无限增加,不会触发拒绝策略
*/
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
- newScheduledThreadPool(int corePoolSize)
/**
*创建一个线程池,可以给定的延迟后运行,或者定期执行。
*/
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
- newSingleThreadExecutor()
/**
*创建一个Executor,它使用一个在无界队列中运行的工作线程。 (但请注意,如果此单个线程由于在关闭
*之前执行期间的故障而终止,则在需要执行后续任务时将使用新的线程。)保证任务顺序执行,并且在任
*何给定的时间不会有多个任务处于活动状态。与 newFixedThreadPool(1)的区别是用
*DelegatedExecutorService包装起来,不让你配置线程池
*/
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
线程个数大小的设置
计算密集型
应用需要非常多的CPU计算资源,在多核CPU时代,我们要让每一个CPU核心都参与计算,将CPU的性能充分利用起来,这样才算是没有浪费服务器配置,如果在非常好的服务器配置上还运行着单线程程序那将是多么重大的浪费。对于计算密集型的应用,完全是靠CPU的核数来工作,所以为了让它的优势完全发挥出来,避免过多的线程上下文切换,比较理想方案是 线程数 = CPU核数+1,也可以设置成CPU核数 * 2
//线程数 = CPU核数+1,也可以设置成CPU核数*2
int poolSize = Runtime.getRuntime().availableProcessors() * 2;
IO密集型
我们现在做的开发大部分都是WEB应用,涉及到大量的网络传输,不仅如此,与数据库,与缓存间的交互也涉及到IO,一旦发生IO,线程就会处于等待状态,当IO结束,数据准备好后,线程才会继续执行。因此从这里可以发现,对于IO密集型的应用,我们可以多设置一些线程池中线程的数量,这样就能让在等待IO的这段时间内,线程可以去做其它事,提高并发处理效率。对于IO密集型应用套用公式
线程数 = CPU核心数/(1-阻塞系数) 这个阻塞系数一般为0.8~0.9之间,也可以取0.8或者0.9
int poolSize = Runtime.getRuntime().availableProcessors() * (1-0.9);
网友评论