【Java】Java核心要点总结 71:ThreadPoolExecutor

简介: 1)为什么需要线程池?重复利用线程资源,减少创建线程和销毁线程系统所花费的开销,也可以限制请求过多带来的系统压力。在一些场景也可以增加处理的速度。2)线程池内部怎么保证线程安全?也就是说一个任务怎么保证不被两个线程都执行?线程池内部有两部分组成一部分是task任务列表 一部分是线程数组,在处理任务是都要去上锁,这个锁其实就是一个变量。等这个任务拿到后再释放锁。

1)为什么需要线程池?

重复利用线程资源,减少创建线程和销毁线程系统所花费的开销,也可以限制请求过多带来的系统压力。在一些场景也可以增加处理的速度。

2)线程池内部怎么保证线程安全?

也就是说一个任务怎么保证不被两个线程都执行?

线程池内部有两部分组成一部分是task任务列表 一部分是线程数组,在处理任务是都要去上锁,这个锁其实就是一个变量。等这个任务拿到后再释放锁。

3)创建线程的方式

阿里公司明确指出不建议使用Executors静态方法创建线程,

比如FixedThreadPool 和 SingleThreadPool 队列长度是integer的最大值,如果控制不好的话容易出现内存溢出。

比如CachedThreadPool 和 ScheduledThreadPool 是创建的线程数量是integer的最大值也有可能导致内存溢出。

通常都会用ThreadPoolExecutor 创建线程池。好处是线程可以控制。

CachedThreadPool底层

·因为没有核心线程,所以任务直接加到SynchronousQueue队列。

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

newSingleThreadExecutor底层

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

4) ThreadPoolExecutor参数使用

corePoolSize 核心线程数会一直存在,除非allowCoreThreadTimeOut设置为true<br />    maximumPoolSize 线程池最大线程数<br />    keepAliveTime:除了核心线程数外的线程 如果没有任务多久释放。<br />    unit:超时时间的单位<br />    workQueue:工作队列,保存未执行的Runnable 任务<br />    threadFactory:创建线程的工厂类<br />    handler:当线程已满,工作队列也满了的时候,会被调用。被用来实现各种拒绝策略。

5) 线程池的工作机制

如果没有空闲的线程执行该任务,并且线程数没有达到核心线程数,会创建一个新的线程。(如果创建的线程存在空闲的就直接用了)。

如果没有空闲的线程执行该任务,当前线程数已经达到核心线程数,会把任务放到任务队里中。

如果没有空闲的线程执行该任务,当前线程数已经达到核心线程数,如果队列满了会创建一个新的线程。

如果没有空闲的线程执行该任务,当前线程数已经到达最大线程数,会通过handler执行拒绝策略。

6)拒绝策略分四种

1.默认直接拒绝抛出ThreadPoolExecutor.AbortPolicy RejectedExecutionException

2.直接不处理ThreadPoolExecutor.DiscardPolicy()

3.把加入队列最早的任务删除。ThreadPoolExecutor.DiscardOldestPolicy()

4.让调用线程池的任务去处理。ThreadPoolExecutor.CallerRunsPolicy()


自定义拒绝策略 实现RejectedExecutionHandler接口,实现抽象方法rejectedExecution方法。

当引用自定义拒绝策略时会初始化自定义拒绝策略类的构造方法。

当线程堵塞触发拒绝策略时会执行rejectedExecution方法。


这几种拒绝策略都是静态内部类实现RejectedExecutionHandler接口。


也可以通过第三方dubbo拒绝策略AbortPolicyWithReport

来处理可以继承AbortPolicy类,重写rejectedExecution

打印出dubbo日志抛出异常

7)队列有哪些?

有五种比较常用的是ArrayBlockingQueue和LinkedBlockingQueue和SynchronousQueue。

ArrayBlockingQueue 是有边界的堵塞队列。

LinkedBlockingQueue 是无边界的堵塞队列,也可以设置边界值。比ArrayBlockingQueue吞吐量要高,原因是ArrayBlockingQueue添加任务和移除任务用的是同一把锁,而LinkedBlockingQueue分别会有一把锁。

SynchronousQueue是是无界的,队列的size始终为0,每个添加任务操作需要等待任务移除操作,反之也是一样。有时我们希望绕开队列,直接分配接收者线程,此时可采用SynchronousQueue,只要当前池的大小还小于最大值,ThreadPoolExecutor就会创建新线程。

SynchronousQueue并不是真正的队列,而是一种管理直接在线程之间移交信息的机制,所有CachedThreadPool用的就是此队列,最大线程数无限大。


参考:

java线程池ThreadPoolExecutor类使用详解 - DaFanJoy - 博客园

相关文章
|
6月前
|
监控 Java 调度
Java线程池ThreadPoolExecutor初略探索
Java线程池ThreadPoolExecutor初略探索
|
存储 Java 调度
Java多线程 ThreadPoolExecutor自定义线程池
Java多线程 ThreadPoolExecutor自定义线程池
573 0
Java多线程 ThreadPoolExecutor自定义线程池
|
6月前
|
监控 安全 Java
深入理解Java线程池:ThreadPoolExecutor
深入理解Java线程池:ThreadPoolExecutor
76 0
|
7月前
|
Java
java 并发之ThreadPoolExecutor
java 并发之ThreadPoolExecutor
56 0
|
4月前
|
Java
|
缓存 Java p3c
【Java用法】线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
【Java用法】线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
199 0
|
消息中间件 缓存 Dubbo
java线程池ThreadPoolExecutor八种拒绝策略浅析
java线程池ThreadPoolExecutor八种拒绝策略浅析
290 0
|
7月前
|
安全 Java 程序员
Java Review - 并发编程_ThreadPoolExecutor原理&源码剖析
Java Review - 并发编程_ThreadPoolExecutor原理&源码剖析
53 0
|
7月前
|
Java
Java【代码分享 10】线程池ThreadPoolExecutor指定线程执行任务(修改线程名称+线程任务指定)
Java【代码分享 10】线程池ThreadPoolExecutor指定线程执行任务(修改线程名称+线程任务指定)
229 0
|
安全 Java
Java Review - 并发编程_ThreadPoolExecutor原理&源码剖析(下)
Java Review - 并发编程_ThreadPoolExecutor原理&源码剖析(下)
74 0
下一篇
DataWorks