ThreadPoolExecutor:线程池不能够使用Executors生成转载

原创
小哥 3年前 (2022-10-28) 阅读数 47 #大杂烩

合理使用线程池可以提供三个好处
一是降低资源消耗。通过重用创建的线程来减少创建和销毁线程造成的消耗。
第二:提高反应速度。当任务到达时,任务可以立即执行,而不需要等待线程创建。
第三:提高线程的可管理性。线程是稀缺资源。如果不加限制地创建它们,不仅会消耗系统资源,还会降低系统的稳定性。线程池可用于统一分配、调优和监控。然而,为了合理地利用线程池,必须熟悉它的原理。
线程池的主工作流。

从上图可以看出,当新任务提交到线程池时,线程池的处理流程如下:

首先,线程池判断基本线程池是否已满?未满,则创建一个工作线程来执行该任务。如果已满,则进入下一进程。
其次,线程池确定工作队列是否已满?如果没有填满,新提交的任务将存储在工作组栏中。如果已满,则进入下一进程。
最后,线程池确定整个线程池是否已满?如果它未满,将创建一个新的工作线程来执行该任务。如果是满的,则给出饱和策略来处理任务。
创建线程池
我们可以通过ThreadPoolExecutor创建一个线程池。

new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
keepAliveTime, milliseconds,runnableTaskQueue, threadFactory,handler);
创建线程池需要几个参数:

corePoolSize - 线程池核心池的大小。
maximumPoolSize - 线程池中的最大线程数。
keepAliveTime - 当线程数大于核心数时,这是额外的空闲线程在终止之前等待新任务的最长时间。
unit - keepAliveTime 时间单位。
workQueue - 用于存储等待执行任务的队列。
threadFactory - 线材工厂。
handler - 拒绝策略。

corePoolSize(线程池的基本大小):当任务提交到线程池时,线程池创建一个线程来执行该任务。即使其他空闲的基本线程可以执行新任务,也会创建线程,直到要执行的任务数大于线程池的基本大小。如果调用线程池prestartAllCoreThreads方法时,线程池将提前创建并启动所有基本线程。
maximumPoolSize(最大线程池大小):线程池允许创建的最大线程数。如果队列已满,并且创建的线程数少于最大线程数,则线程池将创建一个新线程来执行任务。值得注意的是,如果使用无界任务队列参数,则不起作用。
线程池大小

线程池有两个线程计数设置,一个用于核心池线程计数(corePoolSize),一个是最大线程数(maximumPoolSize)。
创建线程池后,默认情况下,线程池中没有线程。只有当有任务时,才能创建线程来执行该任务,除非它被调用。prestartAllCoreThreads()或者prestartCoreThread()方法
当创建的线程数量相等时 corePoolSize 将加入设置的阻塞队列。当队列已满时,将创建线程以执行任务,直到线程池中的数量相等maximumPoolSize。

keepAliveTime(线程活动保持时间):线程池的工作线程在空闲后保持活动的时间量。因此,如果任务较多,且每个任务的执行时间较短,则可以增加这个时间,以提高线程的利用率。
TimeUnit(线程活动保持时间的单位):可选单位有天(DAYS)、工作时间(HOURS)、分钟(MINUTES),毫秒(MILLISECONDS),微秒(MICROSECONDS, 千分之一毫秒)和毫微秒(NANOSECONDS, 千分之一秒)。
runnableTaskQueue(任务队列):保存等待执行的任务的阻塞队列。可以选择以下阻塞队列。
ArrayBlockingQueue:是基于数组结构的有界阻塞队列,按下。 FIFO(先进先出)原则对元素进行排序。
LinkedBlockingQueue:基于列表结构的阻塞队列,按下。FIFO (先进先出) 对元素进行排序,则吞吐量通常较高ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了此队列。
SynchronousQueue:不存储元素的阻塞队列。每个插入操作都必须等到另一个线程调用删除操作,否则插入操作总是被阻塞,吞吐量通常会更高LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了此队列。
PriorityBlockingQueue:支持优先级排序的无界阻塞队列。
DelayQueue: 使用优先级队列实现的无界阻塞队列。
ThreadFactory:用于设置创建螺纹的工厂。您可以为通过线程工厂创建的每个线程设置一个更有意义的名称,Debug并且在定位问题方面非常有帮助。
RejectedExecutionHandler(饱和策略):当队列和线程池已满时,表示线程池已饱和,则必须采用策略来处理提交的新任务。默认情况下,此策略AbortPolicy这意味着当无法处理新任务时会引发异常。以下是JDK1.5提供了四种策略。
ThreadPoolExecutor.AbortPolicy: 丢弃任务和抛出RejectedExecutionException异常。 (默认)
ThreadPoolExecutor.DiscardPolicy:也丢弃任务,但不要抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:放弃队列前面的任务并尝试再次执行该任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:任务由调用线程处理。

当然,也可以根据应用场景的需要来实现。RejectedExecutionHandler接口自定义策略。例如记录或持久化无法处理的任务。

不允许使用线程池。Executors去创建
不允许使用线程池。Executors去创造,但是ThreadPoolExecutor通过这种方式,使得写作学生更加清楚线程池的运行规则,避免了资源耗尽的风险。 说明:Executors每种方法的缺点:

1)newFixedThreadPool和newSingleThreadExecutor:
主要问题是堆叠的请求处理队列可能会消耗非常大量的内存,甚至OOM。
newSingleThreadExecutor
创建单线程线程池。这个线程池只有一个线程在工作,这相当于一个线程连续执行所有任务。如果这个唯一的线程因为异常而结束,将有一个新的线程来取代它。
根据任务提交的顺序,该线程池保证了所有任务的执行顺序。(FIFO, LIFO, 优先级)执行。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
newFixedThreadPool
创建固定大小的线程池。每次提交任务时都会创建一个线程,直到线程达到线程池的最大大小。
一旦达到线程池的最大大小,它将保持不变,如果线程由于执行异常而结束,线程池将添加新的线程。
可以控制并发线程的最大数量,多余的线程将在队列中等待。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}

2)newCachedThreadPool和newScheduledThreadPool:
主要问题是线程的最大数量是Integer.MAX_VALUE,可能会创建非常大量的线程,甚至OOM。
newCachedThreadPool
创建可缓存的线程池。如果线程池的大小超过处理任务所需的线程,
然后是一些闲置的(60当任务数量增加时,线程池可以智能地添加新的线程来处理任务。
该线程池不限制线程池的大小。
线程池大小完全取决于操作系统(或JVM可以创建的最大线程大小。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
newScheduledThreadPool
创建计时线程池以支持定时和定期任务执行
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}

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

public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
如何创建线程池(推荐)
据阿里巴巴称java开发规范,推荐3如何创建线程池

推荐方式1:
首先介绍:commons-lang3包
ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1,
new BasicThreadFactory.Builder().namingPattern("example-schedule-pool-%d").daemon(true).build());

推荐方式 2:
首先介绍:com.google.guava包
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
.setNameFormat("demo-pool-%d").build();

//Common Thread Pool
ExecutorService pool = new ThreadPoolExecutor(5, 200,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());

pool.execute(()-> System.out.println(Thread.currentThread().getName()));
pool.shutdown();//gracefully shutdown

推荐方式 3:
spring配置线程池:自定义线程工厂。bean需要实现ThreadFactory,您可以引用该接口的其他默认实现类,并使用bean
调用execute(Runnable task)方法即可
<bean id="userThreadPool"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">


//in code
userThreadPool.execute(thread);

参考资料来源:http://ifeve.com/java-threadpool/

————————————————
版权声明:本文是CSDN博主“齐言”原创文章,关注。CC 4.0 BY-SA版权协议,转载请附上原始来源链接和本声明。
原始链接:https://blog.csdn.net/fly910905/article/details/81584675

版权声明

所有资源都来源于爬虫采集,如有侵权请联系我们,我们将立即删除

热门