四种线程池类

Java中四种具有不同功能常见的线程池。他们都是直接或者间接配置ThreadPoolExecutor来实现他们各自的功能。这四种线程池分别是
newFixedThreadPool,newCachedThreadPool,newScheduledThreadPool和newSingleThreadExecutor。这四个线程池可以通过Executors类获取。

newFixedThreadPool
通过Executors中的newFixedThreadPool方法来创建,该线程池是一种线程数量固定的线程池。

ExecutorService service = Executors.newFixedThreadPool(4); 

在这个线程池中 所容纳最大的线程数就是我们设置的核心线程数
如果线程池的线程处于空闲状态的话,它们并不会被回收,除非是这个线程池被关闭。如果所有的线程都处于活动状态的话,新任务就会处于等待状态,直到有线程空闲出来。由于newFixedThreadPool只有核心线程,并且这些线程都不会被回收,也就是它能够更快速的响应外界请求。从下面的newFixedThreadPool方法的实现可以看出,newFixedThreadPool只有核心线程,并且不存在超时机制,采用LinkedBlockingQueue,所以对于任务队列的大小也是没有限制的。

   public static ExecutorService newFixedThreadPool(int nThreads) { 
   return new ThreadPoolExecutor(nThreads, nThreads,  0L,
   TimeUnit.MILLISECONDS,  new LinkedBlockingQueue<Runnable>());  }

newCachedThreadPool
通过Executors中的newCachedThreadPool方法来创建。

public static ExecutorService newCachedThreadPool() { 
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 
60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); } 

通过s上面的newCachedThreadPool方法在这里我们可以看出它的 核心线程数为0, 线程池的最大线程数Integer.MAX_VALUE 。而Integer.MAX_VALUE是一个很大的数,也差不多可以说 这个线程池中的最大线程数可以任意大。
当线程池中的线程都处于活动状态的时候,线程池就会创建一个新的线程来处理任务。该线程池中的线程超时时长为60秒,所以当线程处于闲置状态超过60秒的时候便会被回收。 这也就意味着若是整个线程池的线程都处于闲置状态超过60秒以后,在newCachedThreadPool线程池中是不存在任何线程的,所以这时候它几乎不占用任何的系统资源。
对于newCachedThreadPool他的任务队列采用的是SynchronousQueue,上面说到在SynchronousQueue内部没有任何容量的阻塞队列。SynchronousQueue内部相当于一个空集合,我们无法将一个任务插入到SynchronousQueue中。所以说在线程池中如果现有线程无法接收任务,将会创建新的线程来执行任务。

newScheduledThreadPool
通过Executors中的newScheduledThreadPool方法来创建。

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { 
return new ScheduledThreadPoolExecutor(corePoolSize); }
public ScheduledThreadPoolExecutor(int corePoolSize) { 
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, 
new DelayedWorkQueue()); } 

它的核心线程数是固定的,对于非核心线程几乎可以说是没有限制的,并且当非核心线程处于限制状态的时候就会立即被回收。
创建一个可定时执行或周期执行任务的线程池:

ScheduledExecutorService service = Executors.newScheduledThreadPool(4); 
service.schedule(new Runnable() { 
public void run() { 
System.out.println(Thread.currentThread().getName()+"延迟三秒执行"); } 
}, 3, TimeUnit.SECONDS); 
service.scheduleAtFixedRate(new Runnable() { 
public void run() { 
System.out.println(Thread.currentThread().getName()+"延迟三秒后每隔2秒执行"); } 
}, 3, 2, TimeUnit.SECONDS); 

输出结果:

pool-1-thread-2延迟三秒后每隔2秒执行
pool-1-thread-1延迟三秒执行
pool-1-thread-1延迟三秒后每隔2秒执行
pool-1-thread-2延迟三秒后每隔2秒执行
pool-1-thread-2延迟三秒后每隔2秒执行

●schedule(Runnable command, long delay, TimeUnit unit) :延迟一定时间后执行Runnable任务;
●schedule(Callable callable, long delay, TimeUnit unit) :延迟一定时间后执行Callable任务;
●scheduleAtFixedRate(Runnable command, long initialDelay, long
period, TimeUnit unit) :延迟一定时间后,以间隔period时间的频率周期性地执行任务;
●scheduleWithFixedDelay(Runnable command, long initialDelay, long
delay,TimeUnit unit) :与scheduleAtFixedRate()方法很类似,但是不同的是scheduleWithFixedDelay()方法的周期时间间隔是以上一个任务执行结束到下一个任务开始执行的间隔,而scheduleAtFixedRate()方法的周期时间间隔是以上一个任务开始执行到下一个任务开始执行的间隔,也就是这一些任务系列的触发时间都是可预知的。
ScheduledExecutorService功能强大,对于定时执行的任务,建议多采用该方法。

newSingleThreadExecutor
通过Executors中的newSingleThreadExecutor方法来创建,在这个线程池中只有一个核心线程,对于任务队列没有大小限制,也就意味着这一个任务处于活动状态时,其他任务都会在任务队列中排队等候依次执行。
newSingleThreadExecutor将所有的外界任务统一到一个线程中支持,所以在这个任务执行之间我们不需要处理线程同步的问题。

public static ExecutorService newSingleThreadExecutor() { 
return new FinalizableDelegatedExecutorService 
(new ThreadPoolExecutor(1, 1, 
0L, TimeUnit.MILLISECONDS, 
new LinkedBlockingQueue<Runnable>())); 
} 

使用Executors创建线程池的弊端
FixedThreadPool 和 SingleThreadExecutor : 允许请求的队列长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致OOM。
CachedThreadPool 和 ScheduledThreadPool : 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致OOM。
解释:
Executors中创建线程池:
FixedThreadPool 和 SingleThreadExecutor 传入的最后一个参数阻塞队列 “workQueue”,默认的长度是INTEGER.MAX_VALUE,而它们允许的最大线程数量又是有限的,所以当请求线程的任务过多线程不够用时,它们会在队列中等待,又因为队列的长度特别长,所以可能会堆积大量的请求,导致OOM。
CachedThreadPool 和 ScheduledThreadPool 它们的阻塞队列长度有限,但是传入的第二个参数maximumPoolSize 为Integer.MAX_VALUE,这就意味着当请求线程的任务过多线程不够而且队列也满了的时候,线程池就会创建新的线程,因为它允许的最大线程数量是相当大的,所以可能会创建大量线程,导致OOM。

©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页