系统创建一个线程的成本还是比较高的,因为他涉及与系统的交互,在这种情形下,使用线程池可以很好地提高性能,尤其是在程序中需要创建大量生存期比较短暂的线程时,就更应该使用线程池。
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。
好处:
提高响应速度(减少了创建新县城的时间)
降低资源消耗度(重复利用线程池中线程,不需要每次创建)
便于线程管理
使用线程池
ThreadPoolExecutor 是线程池的真正实现,他的构造方法提供了一系列的参数来配置线程池,下面看一下ThreadPoolExecutor的构造方法中各个参数的含义。
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory)
corePoolSize : 线程的核心线程数。默认的情况下,核心线程会在线程池中一直存活,即他们处于闲置状态。如果将 ThreadPoolExecutor 的allowCoreThreadTimeOut()属性设置为 true ,那么闲置的核心线程在等待新任务到来时会有超时策略,这个时间由 keepAliveTime 所指定,当等待的时间超过 keepAliveTime所指定的时长后,核心线程会被终止。
maximumPoolSize : 线程池所能容纳的最大线程数,当活动线程数达到这个值后,后续的任务将会被阻塞。
keepAliveTime : 非核心线程闲置时 的超时时长,超过这个时间,非核心线程将会被回收。当 ThreadPoolExecutor 的 allowCoreThreadTimeOut 属性设置为true时,keepAliveTime 同样会作用于核心线程。
unit : 用于指定 keepAliveTime 的参数单位,这是一个枚举。常用的有 TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)等。
workQueue : 线程池中的任务队列,通过线程池的 execute 方法提交的 Runnable 对象会报存在这个参数中。
theadFactory : 线程工厂,为线程池提供创建线程 新线程的功能。ThreadFactory是一个接口,他只有一个方法,Thread newThread(Runnable r)。
ThreadPoolExecutor 执行任务的时候大致遵循入下规则:
如果线程池中的线程数量未达到 核心线程的数量,那么会直接启动一个核心线程来执行任务。
如果线程池中的核心数量 已经达到或者大于 核心线程数量,那么 任务会被插入到任务队列中排队等待执行。
如果在上一个步骤中 无法将任务插入到任务队列 ,这是由于任务虐队列已满,这个时候如果线程数量未达到线程池数量的最大值,则会启动一个非核心线程来执行任务,否则就会拒绝该任务。
线程池的分类
四种最常见且具有不同功能的线程池,他们都是直接或间接通过配置ThreadPoolExecutor 来实现 自己的功能特性。这四种分别是 FixedThreadPool 、CachedThreadPool、ScheduledThreadPool 以及 SingleThreadExecutor。
FixedThreadPool public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
通过 Executors 的new FixedThreadPool() 创建,他是一种线程数量固定的线程,当线程处于空闲状态时,他们并不会被回收,除非线程池关闭了。当所有线程池处于活动状态时,新任务就会处于等待状态,直到有线程空闲出来。
由于 FixedThreadPool 只有 核心线程并且这些线程不会被回收,这以为这他能够正确的响应外界的请求。
public static void main(String[] args){ ExecutorService executorService = Executors.newFixedThreadPool(2); executorService.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()); } }); executorService.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()); } }); executorService.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()); } }); }
结果如下:
pool-1-thread-1
pool-1-thread-2
pool-1-thread-1
CachedThreadPool
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
通过 Executors 的new CachedThreadPool() 创建,他是一种线程数量不定的线程池,他只有非核心线程,并且最大线程数为 Integer.MAX_VALUE, 由于Integer.MAX_VALUE 是一个很大的数,实际上就相当于任意大。
当线程池中的 线程都处于活动状态时,线程池就会创建新的线程来处理新的任务。否则就用空线程来处理任务。
线程池中空线程都有超时机制,这个超时时长为60秒,超过60秒闲置线程就会被回收。
从 CachedThreadPool 的特性上来看,这类线程池比较适合执行 大量的耗时较少的任务。当整个线程池中都处于闲置状态时,线程池中的线程就会因为超时而停止。这个时候 CachedThreadPool 之中是没有任何线程的,他几乎是不占用任何系统资源的。
ExecutorService executorService = Executors.newCachedThreadPool(); executorService.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()); } });
ScheduledThreadPool public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize); } public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS, new DelayedWorkQueue()); }
通过 Executors 的new ScheduledThreadPool() 创建, 他的核心线程数量是固定的,而非核心线程数 是没有限制的,并且当非核心线程 闲置时 会被立刻进行回收。
ScheduledThreadPool 这类线程主要用于指定定时任务和具有固定周期的重复任务。
public static void main(String[] args){ ScheduledExecutorService executorService = Executors.newScheduledThreadPool(2); executorService.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()); } }); }
SingleThreadExecutor public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
通过 Executors 的new SingleThreadExecutor() 创建,这类线程内部只有一个核心线程,他确保所有的任务都是在同一个线程按顺序执行,
SingleThreadExecutor 的意义在于统一外界的任务到一个线程中,这使得这些任务之间不需处理同步的问题。
ExecutorService executorService = Executors.newSingleThreadExecutor(); executorService.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()); } }); executorService.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()); } });
结果如下:
pool-1-thread-1
pool-1-thread-1
上面对 常见的四种线程 进行了详细的介绍,除了上面系统提供的四种线程外,也可以根据时间需要灵活的配置线程。