6

Java并发线程之线程池

 3 years ago
source link: https://segmentfault.com/a/1190000039076177
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

初始化线程池后,把任务丢进去,等待调度就可以了,使用起来比较方便。

JAVA中 Thread 是线程类,不建议直接使用 Thread 执行任务,在并发数量比较多的情况下,每个线程都是执行一个很短的时间就任务结束了,这样频繁创建线程会大大降低系统的效率,因为频繁的创建和销毁线程需要时间。而线程池可以复用,就是执行完一个任务,并不销毁,而是可以继续执行其它任务。

Thread的弊端

new Thread()

线程池的优点

  • 重用存在的线程,减少对象创建,消亡的开销,性能佳,降低资源消耗。
  • 可以控制最大并发线程数,提高系统资源利用率,同时避免过多资源竞争,避免阻塞,提高响应速度。
  • 提供定时执行,定期执行,单线程,并发数控制等功能,以提高线程的可管理性。

Executors利用工厂模式向我们提供了4种线程池实现方式,但是并不推荐使用,原因是使用Executors创建线程池不会传入相关参数而使用默认值所以我们常常忽略了那些重要的参数(线程池大小、缓冲队列的类型等),而且默认使用的参数会导致资源浪费,不可取。

ThreadPoolExecutor 介绍

构造函数和参数

QJBfqmq.jpg!mobile

java.uitl.concurrent.ThreadPoolExecutor 类是线程池中最核心的一个类。

public class ThreadPoolExecutor extends AbstractExecutorService {
    /** 构造函数 1 */
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {}
                              
    /** 构造函数 2 */
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {}
                              
    /** 构造函数 3 */
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) {}
                              
    /** 构造函数 4 */
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {}
}

ThreadPoolExecutor类中提供了四个构造方法,在构造函数4中,参数最多,通过观察其他3个构造函数,发现前面三个构造器都是调用的第四个构造器进行的初始化工作。

YjAvInz.jpg!mobile

构造器中各个参数的含义

corePoolSize 核心线程池的大小,在创建了线程池后,默认情况下,线程池中没有任何的线程池,而是等任务过来了再去创建线程执行任务。除非调用了预创建线程的方法,即在没有任务到来之前就创建 corePoolSize 个线程或者一个线程。当线程池中的线程数量到达 corePoolSize 后,就会把到达的任务放到缓存队列里面。

  • prestartCoreThread() : 预创建一个核心线程,使其闲置等待工作。
  • prestartAllCoreThreads() : 启动所有核心线程,导致它们空闲地等待工作。
maxnumPoolSize 线程池中最大的线程数,是一个非常重要的参数,它表示在线程池中最多能创建多少线程。

keepAliveTime表示线程在没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于 corePoolSize 时, keepAliveTime 才会起作用,即当线程池中的线程数大于 corePoolSize ,如果一个线程的空闲时间达到 keepAliveTime ,则会终止直到线程池中的线程数量不大于 corePoolSize 。但是如果调用了 allowCoreThreadTimeOut(boolean) 方法,在线程池中线程数不大于 corePoolSize 时, keepAliveTime 参数也会启作用,直到线程池中的线程数为0。

unit参数 keepAliveTime 的时间单位,有7种取值,在 TimeUnit 类中有7种静态属性。

  • TimeUnit.DAYS : 以 天 为单位 ;
  • TimeUnit.HOURS : 以 小时 为单位 ;
  • TimeUnit.MINUTES : 以 分钟 为单位 ;
  • TimeUnit.SECONDS : 以 秒 为单位 ;
  • TimeUnit.MILLISECONDS : 以 毫秒 为单位 ;
  • TimeUnit.MICROSECONDS : 以 微秒 为单位 ;
  • TimeUnit.NANOSECONDS : 以 纳秒 为单位 ;

workQueue 一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一般有以下几种选择。

Integer.MAX_VALUE
threadFactory 线程工厂,主要用来创建线程。线程池最重要的一项工作,就是在满足某些条件情况下创建线程。在 ThreadPoolExecutor 线程池中,创建线程的操作时交给 ThreadFactoty 来完成。使用线程池,就必须要指定 threadFactory 。如果我们的构造器中没有指定使用 ThreadFactory ,这个时候 ThreadPoolExecutor 就会使用默认的 ThreadFactory:DefaultThreadFactory

handler在ThreadPoolExecutor线程池中还有一个重要的接口:RejectedExecutionHandler。当提交给线程池的某一个新任务无法直接被线程池中“核心线程”直接处理,又无法加入等待队列,也无法创建新的线程执行;又或者线程池已经调用shutdown()方法停止了工作;又或者线程池不是处于正常的工作状态;这时候ThreadPoolExecutor线程池会拒绝处理这个任务,触发创建ThreadPoolExecutor线程池时定义的RejectedExecutionHandler接口的实现,表示当拒绝处理任务时的策略,有以下四种取值,四种值都为其静态内部类:

  • ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常
  • ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
  • ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行新提交的任务。

ThreadPoolExecutor 执行 execute 方法分下面4种情况

  • 如果当前运行的线程少于 corePoolSize ,则创建新的线程来执行任务(执行这一步骤需要获取全局锁)
  • 如果运行的线程等于或者多于 corePoolSize ,则将任务加入到 BlockingQueue
  • 如果无法将任务加入 BlockingQueue (队列已满),则创建新的线程来处理任务(执行这一步骤需要获取全局锁)
  • 如果创建新线程将当前运行的线程超出 maxnumPoolSize ,任务被拒绝,并调用 RejectedExecutionHandler.rejectedExecution() 方法。

关注微信公众号:【入门小站】,解锁更多知识点

aMVjaae.png!mobile


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK