Java线程池Executors

简介:

一 简述

   线程池,作为一个管理一组同构工作线程的资源。接受提交的任务,利用线程池中的线程进行工作的处理。 在另一篇《Java多线程设计模式(4)线程池模式》利用非Executors描述了线程池基本构建过程,对于线程池基本机制进行了说明。由于Java类库中有Executor来专门用于线程池的管理的类,所以可以用Executor任务执行框架来实现线程池的构建。

   Executor核心的思想就是将请求处理任务的提交线程和任务的实际执行解耦开来。利用execute来传递一个具体执行的Runnable任务类,或者利用submit来传递一个Runnable任务类或Callable获取任务返回值的任务。

   对于每次通过execute方法提交的任务执行顺序如下:

   1、会判断当前池线程以及核心数目的大小,当池中当前的线程数小于核心线程数时,会创建新的线程。具体创建新线程流程如:获取内置锁,将任务添加到内部的BlockingQueue任务队列中,再利用工厂方法产生一个执行该任务的线程,这个线程是非守护及优先级是NORM的线程。

   2、当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。

   3、当线程数大于等于核心线程数,且任务队列已满,采用以下处理方式

      若线程数小于最大线程数,创建线程

      若线程数等于最大线程数,抛出异常,拒绝任务,进行饱和处理策略。

   Executor任务执行框架类图如下:

   

二 ThreadPoolExecutor线程池

    一般线程池可以直接利用构造器来实例化一个ThreadPoolExecutor,也可以利用Executors工具类来创建一个线程池。也可以通过继承扩展ThreadPoolExecutor来自定义一些ThreadPoolExecutor子类。

在利用Executors静态生成ThreadPoolExecutor的时候,都会在内部实例化一个ThreadPoolExecutorThreadPoolExecutor内部都是用BlockingQueue队列来保存提交的任务Runnable

    一个ThreadPoolExecutor需要考虑三个方面

    一个是线程池的大小,二个是任务队列的大小,三个是饱和策略

        常见的用法就是利用Executors的静态工厂来创建。

        标准的ThreadPoolExecutor构造方法如下:

1
2
3
4
5
6
7
public  ThreadPoolExecutor( int  corePoolSize,
         int  maximumPoolSize,
         long  keepAliveTime,
         TimeUnit unit,
         BlockingQueue<Runnable> workQueue,
         ThreadFactory threadFactory,
         RejectedExecutionHandler handler)


ThreadPoolExecutor的几个参数说明:

corePoolSize

核心线程数,核心线程会一直存活,即使没有任务需要处理。当线程数小于核心线程数时,即使现有的线程空闲,线程池也会优先创建新线程来处理任务,而不是直接交给现有的线程处理。

核心线程在allowCoreThreadTimeout被设置为true时会超时退出,默认情况下不会退出。

maxPoolSize

当线程数大于或等于核心线程,且任务队列已满时,线程池会创建新的线程,直到线程数量达到maxPoolSize。如果线程数已等于maxPoolSize,且任务队列已满,则已超出线程池的处理能力,线程池会拒绝处理任务而抛出异常。

keepAliveTime

当线程空闲时间达到keepAliveTime,该线程会退出,直到线程数量等于corePoolSize。如果allowCoreThreadTimeout设置为true,则所有线程均会退出直到线程数量为0

allowCoreThreadTimeout

是否允许核心线程空闲退出,默认值为false

queueCapacity

任务队列容量。从maxPoolSize的描述上可以看出,任务队列的容量会影响到线程的变化,因此任务队列的长度也需要恰当的设置。


Executors静态工厂创建线程池解释

Executors.newCachedThreadPool();

这种会创建一个目标数目或核心数目为0,最大数目为Integer.MAX_VALUE超时设置1分钟,并且利用SynchronousQueue来存储任务的ThreadPoolExecutorSynchronousQueue是一种同步移交队列,任务的生产者线程直接将任务移交给任务的消费者线程即工作线程。对于很大的线程池,可以利用SynchronousQueue避免任务的排队。要想将一个元素放入该队列,必须有另一个线程正在等待接收这个元素。当一个新的任务提交后,如果没有线程在等待,且线程池的当前值大小小于最大值,一般ThreadPoolExecutor都会创建一个新的线程。否则就会调用饱和策略来处理。SynchronousQueue这种队列一般用于当线程池无界或者很大的时候采用,目的就是更快的提交任务,充分利用线程池中的工作线程。

Executors.newFixedThreadPool(n);

这种会创建核心数目以及最大数目都是指定初始值的线程池,线程不会超时也就是不会被回收,并且利用无界的(最大值是Integer.MAX_VALUE)LinkedBlockingQueue来存储任务的ThreadPoolExecutor

Executors.newSingleThreadExecutor();

这种会创建核心数目以及最大数目都是1的线程池,线程不会超时也就是不会被回收,并且利用无界的(最大值是Integer.MAX_VALUE)LinkedBlockingQueue来存储任务的ThreadPoolExecutor

饱和策略解释:
当有界队列被填满后,就需要考虑如何对于再次发送的请求处理。

ThreadPoolExecutor中可以通过setRejectedExecutionHandler设置饱和策略。

ThreadPoolExecutor中包含了四种饱和策略:

AbortPolicy, DiscardPolicyDiscardOldestPolicyCallerRunsPolicy

AbortPolicy,即中止策略,是默认的饱和策略,该策略将抛出未检查的RejectedExecutionException

DiscardPolicy,即抛弃策略,会丢弃队列满后请求的任务。

DiscardOldestPolicy,即抛弃最旧的策略,会抛弃下一个将要被执行的任务,然后尝试重新提交新任务。

CallerRunsPolicy即调用者策略,既不会抛弃任务,也不会抛出异常,而是将任务回退到调用者。它不会在线程池的某个线程执行新提交的任务,而是在一个调用execute的线程中执行该任务。


注意:

1、可以利用ArrayBlockingQueue,有界的LinkedBlockingQueuePriorityBlockingQueue来设置存储任务的队列界限。

2、利用Executors静态工厂方法创建ThreadPoolExecutor或者直接实例化ThreadPoolExecutor的对象,默认初始的时候线程并不会立即启动,而是等到有任务提交时候才会启动。当然可以调用prestartAllCoreThreads来启动所有的核心线程。

3、在使用线程池中,当任务是相互独立且类型基本上相同的时候,此时才可以设置线程池和工作队列的界限。




本文转自 zhao_xiao_long 51CTO博客,原文链接:http://blog.51cto.com/computerdragon/1212442


相关文章
|
3月前
|
Java 调度 数据库
Java并发编程:深入理解线程池
在Java并发编程的海洋中,线程池是一艘强大的船,它不仅提高了性能,还简化了代码结构。本文将带你潜入线程池的深海,探索其核心组件、工作原理及如何高效利用线程池来优化你的并发应用。
|
3月前
|
存储 监控 Java
Java多线程优化:提高线程池性能的技巧与实践
Java多线程优化:提高线程池性能的技巧与实践
106 1
|
3月前
|
安全 Java 数据库
一天十道Java面试题----第四天(线程池复用的原理------>spring事务的实现方式原理以及隔离级别)
这篇文章是关于Java面试题的笔记,涵盖了线程池复用原理、Spring框架基础、AOP和IOC概念、Bean生命周期和作用域、单例Bean的线程安全性、Spring中使用的设计模式、以及Spring事务的实现方式和隔离级别等知识点。
|
3月前
|
存储 监控 安全
一天十道Java面试题----第三天(对线程安全的理解------>线程池中阻塞队列的作用)
这篇文章是Java面试第三天的笔记,讨论了线程安全、Thread与Runnable的区别、守护线程、ThreadLocal原理及内存泄漏问题、并发并行串行的概念、并发三大特性、线程池的使用原因和解释、线程池处理流程,以及线程池中阻塞队列的作用和设计考虑。
|
12天前
|
监控 安全 Java
在 Java 中使用线程池监控以及动态调整线程池时需要注意什么?
【10月更文挑战第22天】在进行线程池的监控和动态调整时,要综合考虑多方面的因素,谨慎操作,以确保线程池能够高效、稳定地运行,满足业务的需求。
91 38
|
12天前
|
Prometheus 监控 Cloud Native
JAVA线程池监控以及动态调整线程池
【10月更文挑战第22天】在 Java 中,线程池的监控和动态调整是非常重要的,它可以帮助我们更好地管理系统资源,提高应用的性能和稳定性。
43 4
|
12天前
|
Prometheus 监控 Cloud Native
在 Java 中,如何使用线程池监控以及动态调整线程池?
【10月更文挑战第22天】线程池的监控和动态调整是一项重要的任务,需要我们结合具体的应用场景和需求,选择合适的方法和策略,以确保线程池始终处于最优状态,提高系统的性能和稳定性。
72 2
|
15天前
|
缓存 监控 Java
java中线程池的使用
java中线程池的使用
|
2月前
|
Java 调度 开发者
Java并发编程:深入理解线程池
在Java的世界中,线程池是提升应用性能、实现高效并发处理的关键工具。本文将深入浅出地介绍线程池的核心概念、工作原理以及如何在实际应用中有效利用线程池来优化资源管理和任务调度。通过本文的学习,读者能够掌握线程池的基本使用技巧,并理解其背后的设计哲学。
|
2月前
|
缓存 监控 Java
Java中的并发编程:理解并应用线程池
在Java的并发编程中,线程池是提高应用程序性能的关键工具。本文将深入探讨如何有效利用线程池来管理资源、提升效率和简化代码结构。我们将从基础概念出发,逐步介绍线程池的配置、使用场景以及最佳实践,帮助开发者更好地掌握并发编程的核心技巧。