new Thread的弊端
①.每次new Thread新建对象,性能差。
②.线程缺乏同意管理,可能无限制的新建线程,相互竞争。有可能占用过多系统资源导致死机或OOM(程序申请内存过大)。
③.缺少更多功能,如更多执行,定期执行 ,线程中断。
线程池的好处
①.重用存在的线程,减少对象的创建和消亡的开销,性能好。
②.可有效控制最大并发线程数,提高系统资源利用率,同时可以避免过多资源竞争,避免阻塞。
③.提供定时执行,定期执行,单线程,并发数控制等功能。
一.线程池--ThreadPoolExecutor
1.可接受的初始化参数
①.corePoolSize:核心线程数量
②.maximumPoolSize:线程最大线程数
③.workQueue:阻塞队列。存储等待执行的任务,很重要,会对线程池运行过程产生重大影响。
如果运行的线程数少于corePoolSize的时候,直接创建新线程来处理任务,即使线程池中其它线程是空闲的;
如果运行的线程数大于等于corePoolSize的时候,且小于maximumPoolSize,则只有当workQueue满了的时候,才创建新的线程来处理任务;
如果设置的corePoolSize和maximumPoolSize相同,那么创建的线程池大小是固定的。这个时候如果有新任务提交,并且workQueue没满的时候,就把请求放入到workQueue里面,等待有空闲的线程,从里面取出进行处理;
如果我们运行的线程数量大于maximumPoolSize,workQueue也已经满了的时候,那么通过拒绝策略参数来处理。
④.keepAliveTime:线程没有任务执行时最多保持多久时间终止。
⑤.unit:keepAliveTime的时间单位。
⑥.threadFactory:线程工厂,用来创建线程。
⑦.rejectHandle:当拒绝处理任务时的策略。
如果workQueue满了并且没有空闲线程时,这时候如果还在提交任务,就需要策略来处理。
常用的策略:
默认策略是直接抛异常;
丢弃掉阻塞队列中最靠前的任务;
用调用者所在线程来执行任务;
直接丢弃当前任务;
2.线程池的几种状态
RUNNING:接受新提交的任务,也能处理阻塞队列里的任务。
SHUTDOWN:关闭状态。当处于SHUTDOWN状态时,不能接收新提交的任务,但是可以继续处理阻塞队列中已经保存的任务。在线程池处于 RUNNING状态时,调用shutdown()方法会使线程进入该状态。
STOP:不能接收新提交的任务,也不可以继续处理阻塞队列中已经保存的任务。会中断正在处理任务的线程。调用shutDownNow()方法时,会进入该状态。
TIDYING:如果所有任务都终止,线程池中工作线程数量为0,会进入该状态。
TERMINATED:之后调用terminated()方法,会进入TERMINATED状态。
3.提供的一些方法
①.execute():提交任务,交给线程池执行。
②.submit():提交任务,能够返回执行结果。相当于execute+Future
③.shutdown():关闭线程池,前提是等待任务都执行完。
④.shutdownNow():关闭线程池,不等待任务都执行完。
⑤.getTaskCount():线程池已执行和未执行的任务总数。
⑥.getCompletedTaskCount():线程池已完成的任务数量。
⑦.getPoolSize():线程池当前的线程数量。
⑧.getActiveCount():当前线程池中正在执行任务的线程数量。
二.线程池--线程池类图
JUC里面有3个Excutor接口,分别是Executor,ExecutorService,ScheduledExecutorService
Executor:运行新任务的简单接口。
ExecutorService:扩展了Executor,添加了一些用来管理生命周期的方法。
ScheduledExecutorService:扩展了ExecutorService,支持Future和定期执行。
三.线程池--Excutor框架接口
1.Executors.newCachedThreadPool():创建一个可缓存的线程池。可以灵活回收。
2.Executors.newFixedThreadPool():创建一个定长的线程池。
3.Executors.newScheduledThreadPool():创建一个定长的线程池。支持定时,周期性任务执行。
4.Executors.newSingleThreadExecutor():创建一个单线程化的线程池。只会用唯一的线程来执行任务。
四.线程池--线程池配置
1.CPU密集型任务,就需要尽量压榨CPU,参考值可以设为NCPU+1。
2.IO密集型任务,参考值可以设置为2*NCPU