Java中线程池是运用场景的并发框架,几乎所需要异步或并发执行程序都可以使用线程池。合理使用线程池可以带来以下好处:
1. 降低资源消耗。通过重复利用以创建的线程降低线程的创建和销毁造成的消耗。
2. 提高响应速度。当任务到达时,任务可以不需要等到线程的创建就能立即执行。
3.提高线程的可管理性。 线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。
线程池主要处理流程:
ThreadPoolExecutor执行execute方法分下面4种情况:
1) 如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(注意,执行这一步骤需要获取全局锁)
2) 如果运行的线程等于或多于corePoolize,则将任务加入BlockingQueue
3) 如果无法将任务加入BlockingQueue(队列已满),则创建新的线程来处理任务(注意,执行这一步骤需要加入全局锁)
4)如果创建新线程将使当前运行的线程超出maximunPoolSize,任务将被拒绝,并调用RejectedExecutionHandler.rejectedExecution()方法
ThreadPoolExecutor采取来上述步骤整体设计思路,是为了在执行execute()方法时,近可能地避免获取全局锁(性能瓶颈),在ThreadPoolExecutor完成预热之后,几乎所有的execute()方法调用
都是执行步骤2,而步骤2不需要获取全局锁。
线程池构造器:
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler)
线程构造需要如下输入参数:
1).corePoolSize(线程池基本大小)当提交一个任务到线程池中,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行也会创建线程,等到需要执行的任务数大于当前线程池基本大小时就不再创建。
2) .workQueue(任务队列):用于保存等待执行的任务的阻塞队列。
3).maximumPoolSize:线程池允许创建的最大线程数。
4). RejectedExecutionHandler(饱和策略):当队列和线程池都满了,说明线程池处于饱和状态,那么必须采用一种策略处理提交的新任务。默认策略AbortPolicy.
AbortPolicy:直接抛出异常。
CallerRunsPolicy:只用调用者线程来运行任务。
DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
DiscardPolicy:不处理,丢弃掉。
5).keepAliveTime(线程活动保持时间):线程池的工作线程空闲后,保持存活的时间。所以,如果任务多,并且每个任务执行的时间比较短,可以调大时间,提高线程的利用率。
6).TimeUnit(线程活动保持的单位)。
向线程池提交任务:
可以使用两个方法向线程池提交任务,分别为execute()和submit()方法。
execute()方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功。通过以下代码可知execute()方法输入的任务是一个Runnablel类的实例。
submit()方法用于提交需要返回值的任务。线程池会返回一个future类型的对象,通过这个future对象可以判断任务是否执行成功,并且可以通过future的get()方法来获取返回值,get()方法
来获取返回值,get()方法会阻塞当前线程直到任务结束,而使用get(long timeout,TimeUnit unit)方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务还没有完成。
关闭线程池
可以通过调用线程池的shutdown或shutdownnNow方法来关闭线程池。 它门的原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止。但是它门存在一定的区别,shutDownNow首先将线程池的状态设置成stop,然后尝试停止所有的正在执行或暂停的线程,并返回等待执行线程列表,而shutdown只是将线程池的状态设置成shutdown状态,然后中断所有没有执行任务线程。