Java常见线程池


Java线程池创建

0.回顾

之前一篇讲的比较乱,主要整理自别人的。这次自己记一下想要说的问题。

想要理解的就是,常用线程池,手动创建线程池,关于核心线程和工作线程。

以及补充一下阻塞队列,还有拒绝策略。

1.常用线程池

  • newCachedThreadPoolCached,可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
  • newFixedThreadPoolFixed,定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
  • newSceduledThreadPoolScheduled,定长线程池,支持定时及周期性任务执行。
  • newSingleThreadExecutorSingle,单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

2.详细参数

其实主要就是看一下他们的创建方式。

2.0 手动创建线程池

首先回忆一下手动创建线程池的重要参数:

1
2
3
4
5
6
7
8
9
10
11
/**
* 用给定的初始参数创建一个新的ThreadPoolExecutor。
*/
public ThreadPoolExecutor(int corePoolSize,//线程池的核心线程数量
int maximumPoolSize,//线程池的最大线程数
long keepAliveTime,//当线程数大于核心线程数时,多余的空闲线程存活的最长时间
TimeUnit unit,//时间单位
BlockingQueue<Runnable> workQueue,//任务队列,用来储存等待执行任务的队列
ThreadFactory threadFactory,//线程工厂,用来创建线程,一般默认即可
RejectedExecutionHandler handler//拒绝策略,当提交的任务过多而不能及时处理时,我们可以定制策略来处理任务
)
  • 核心线程数
  • 最大线程数
  • 存活时间,时间单位
  • 阻塞队列
  • 线程工厂(可忽略):用来设置线程名字的,默认为 pool-线程池数-thread-线程数
  • 拒绝策略(可忽略):默认拒绝策略为AbortPolicy,即 丢弃任务并抛出异常 RejectedExecutionException

2.0.1 拒绝策略

拒绝策略有四种,简单提一下:

  1. AbortPolicy :丢弃任务,抛出异常

  2. DiscardPolicy :丢弃任务,静默丢弃

  3. DiscardOledestPolicy丢弃队列中最老的未处理请求,然后再次尝试提交新任务。

  4. CallerRunsPolicy :在调用者线程中,运行当前被丢弃的任务。

也就是说,两个丢弃当前任务(不管新任务);
一个丢弃队列中最老任务(新任务重要老任务可以不管);
一个让别人执行任务(调用当前线程的线程);

2.0.2 阻塞队列

阻塞队列中的有界无界就是说最大存储容量,但是是理想的。无界一般都是指 Integer.MAX_VALUE ,int的最大值个数。有界就是自己来指定容量。

1.SynchronousQueue

SynchronousQueue 是一个没有数据缓冲的同步队列,不存储元素。

每一个put操作必须等待一个take操作,否则不能继续添加元素。

常用于生产者消费者问题,负责把生产者线程处理的数据直接传递给消费者线程。

2. LinkedBlockingQueue

LinkedBlockingQueue ,基于链表(linked nodes)的可选界(optionally-bounded)的双向阻塞队列,线程安全。

可以对first、last元素进行操作,两端都可以入队出队。

默认容量为 Integer.MAX_VALUE ,无界队列。

3.DelayedWorkQueue

DelayedWorkQueue ,一个定制的专门用于存储Runnable任务的队列。

优先级队列,基于堆结构的等待队列,最小堆。

最近要到达时间的节点放在堆顶,每个节点都会附带到期时间,依次作为堆调整的依据。

2.1 Cached

Cached,缓存的;

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

分析一下其参数:

  • 核心线程:0个,也就是说
  • 最大线程数:Integer的最大值,反正也是很多了
  • 存活时间、单位:60秒
  • 阻塞队列:SynchronousQueue ,同步队列,无容量

这样的话,其关键在于核心线程0个和这个同步队列

来一个线程就创建一个非核心线程,因为队列中不存储任何元素。

用完1分钟后销毁,所以说是Cached缓存一下。

2.2 Fixed

Fixed,固定的(固定数量的)

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

参数:

  • 核心线程:传入的n
  • 最大线程:传入的n
  • 存活时间、单位:0毫秒
  • 阻塞队列:LindedBlockingQueue ,链表双向阻塞队列

这个的关键是在 核心线程数==最大线程数 ,所有的线程都是核心线程,不会销毁。

然后不考虑Integer最大值的话,是无界的,也就是说使用核心线程去处理所有任务

其中的线程数是固定的不会销毁的,所以说是Fixed。

2.3 Scheduled

Scheduled,预定的;

1
2
3
4
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}

参数:

  • 核心线程:传入的c
  • 最大线程:Integer最大值
  • 存活时间、单位:0纳秒
  • 阻塞队列: DelayedWorkQueue ,延迟队列,小根堆。

其关键在于这个队列,优先级队列小根堆

这是一个可以定时、周期性执行的线程池,它继承FutureTask并实现了接口RunnableScheduledFuture

里面有个参数time,指的是执行时间。有个函数setNextRunTime(),可以设置下次执行时间。

小根堆的优先级就是根据时间来排序的。

2.4 Single

Single,单例

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

参数:

  • 核心线程:1
  • 最大线程:1
  • 存活时间、单位:0毫秒
  • 阻塞队列:LinkedBlockingQueue

其关键在于,核心线程和最大线程都是1。

而且是无界队列,也就是说只有一个线程存活并执行。其他任务都排队。

所以说是Single。

3. 补充

有一点比较重要的,之前一直理解错了。。。

也就是说线程池中的核心线程,和非核心线程,并没有什么区别,他们都是普通线程,并没有什么参数来区分他们。

在回收线程非核心线程的时候,看谁闲着并且超过存活时间了,就去回收它。

啊,就这样吧。


文章作者: SongX64
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 SongX64 !
  目录