java线程池及线程池运行流程

一、线程池的优势

1.减少资源消耗 :通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

2.提高响应速度:当任务到达时,任务可以不需要等到线程创建就能立即执行。

3.方便管理:对线程进行统一的分配,监控和调优。

二、线程池运行流程

1. 可能大家已经了解了线程池的参数,但是对线程池整体的运行流程还不是很清晰,下面我将利用去银行取款的场景来模拟线程池的运行,本文的核心,希望仔细阅读,必有收获。

2. 参数对照

        所有的柜台 = 线程池

        核心线程数 = 所有普通银行柜员
        最大线程数 = 银行大厅经理
        队列 = 银行的座位
        保安 = 拒绝策略

        经理观察时间 = keepAliveTime
        时间单位 = unit
3. 运行流程介绍

step1 : 假设有一天你去银行取钱,正常情况我们就去柜台办理。
类比程序:相当于,一个任务来了,我们直接交给核心线程去处理。


step2: 假设我们看到所有柜员都在在办理业务,那我们只能在座位上等待。
类比程序:相当于,核心线程数满了,任务就被放到队列里面去了。


step3: 假设所有座位也坐满了,这时候大厅经理发现人太多了,他说我开一个vip窗口吧,我也帮忙处理一下,然后叫座位上的人来他的vip窗口办理业务。
类比程序:相当于核心线程数满了,队列也满了,那我们就把任务放到最大线程数里面。


step4: 假设所有柜台也有人,座位也满了,vip窗口也在办理,保安发现在大厅外还有人想进来办理业务,这时候保安说我得保证大厅内正常的运行,外面人不允许进来了。
类比程序:相当于核心线程数满了,队列也满了,最大线程数也满了,那么我们就执行拒绝策略

step5: 假设办理高峰期过了,经理发现,座位已经空出来了,正常的柜员办理业务就够用了,于是他决定在vip柜台观察10分钟,如果没有人没有把座位再次坐满的话,他就有撤销vip窗口了。
类似程序:相当于队列已经不满了,最大线程数已经停止工作了,并且等待了10(keepAliveTime)分钟(unit), 都没有高峰期了,那么线程池将收回最大线程数保留核心线程数
 

三、线程池的参数

    /**
     * Creates a new {@code ThreadPoolExecutor} with the given initial
     * parameters.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @param maximumPoolSize the maximum number of threads to allow in the
     *        pool
     * @param keepAliveTime when the number of threads is greater than
     *        the core, this is the maximum time that excess idle threads
     *        will wait for new tasks before terminating.
     * @param unit the time unit for the {@code keepAliveTime} argument
     * @param workQueue the queue to use for holding tasks before they are
     *        executed.  This queue will hold only the {@code Runnable}
     *        tasks submitted by the {@code execute} method.
     * @param threadFactory the factory to use when the executor
     *        creates a new thread
     * @param handler the handler to use when execution is blocked
     *        because the thread bounds and queue capacities are reached
     * @throws IllegalArgumentException if one of the following holds:
* {@code corePoolSize < 0}
* {@code keepAliveTime < 0}
* {@code maximumPoolSize <= 0}
* {@code maximumPoolSize < corePoolSize} * @throws NullPointerException if {@code workQueue} * or {@code threadFactory} or {@code handler} is null */ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.acc = System.getSecurityManager() == null ? null : AccessController.getContext(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }

corePoolSize(必需):核心线程数。默认情况下,核心线程会一直存活,但是当将 allowCoreThreadTimeout 设置为 true 时,核心线程也会超时回收。
maximumPoolSize(必需):线程池所能容纳的最大线程数。当活跃线程数达到该数值后,后续的新任务将会阻塞。
keepAliveTime(必需):线程闲置超时时长。如果超过该时长,非核心线程就会被回收。如果将 allowCoreThreadTimeout 设置为 true 时,核心线程也会超时回收。
unit(必需):指定 keepAliveTime 参数的时间单位。常用的有:TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)、TimeUnit.MINUTES(分)。
workQueue(必需):任务队列。通过线程池的 execute() 方法提交的 Runnable 对象将存储在该参数中。其采用阻塞队列实现。
threadFactory(可选):线程工厂。用于指定为线程池创建新线程的方式。
handler(可选):拒绝策略。当达到最大线程数时需要执行的饱和策略。


四、参数详解

1.任务队列(workQueue):

ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列(数组结构可配合指针实现一个环形队列)。
LinkedBlockingQueue: 一个由链表结构组成的有界阻塞队列,在未指明容量时,容量默认为 Integer.MAX_VALUE。
PriorityBlockingQueue: 一个支持优先级排序的无界阻塞队列,对元素没有要求,可以实现 Comparable 接口也可以提供 Comparator 来对队列中的元素进行比较。跟时间没有任何关系,仅仅是按照优先级取任务。
DelayQueue:类似于PriorityBlockingQueue,是二叉堆实现的无界优先级阻塞队列。要求元素都实现 Delayed 接口,通过执行时延从队列中提取任务,时间没到任务取不出来。
SynchronousQueue: 一个不存储元素的阻塞队列,消费者线程调用 take() 方法的时候就会发生阻塞,直到有一个生产者线程生产了一个元素,消费者线程就可以拿到这个元素并返回;生产者线程调用 put() 方法的时候也会发生阻塞,直到有一个消费者线程消费了一个元素,生产者才会返回。
LinkedBlockingDeque: 使用双向队列实现的有界双端阻塞队列。双端意味着可以像普通队列一样 FIFO(先进先出),也可以像栈一样 FILO(先进后出)。
LinkedTransferQueue: 它是ConcurrentLinkedQueue、LinkedBlockingQueue 和 SynchronousQueue 的结合体,但是把它用在 ThreadPoolExecutor 中,和 LinkedBlockingQueue 行为一致,但是是无界的阻塞队列。

2.拒绝策略(handler)

AbortPolicy(默认):丢弃任务并抛出 RejectedExecutionException 异常。

CallerRunsPolicy:由调用线程处理该任务。   

DiscardPolicy:丢弃任务,但是不抛出异常。可以配合这种模式进行自定义的处理方式。

DiscardOldestPolicy:丢弃队列最早的未处理任务,然后重新尝试执行任务。

3.线程工厂(threadFactory)

默认已帮我们实现,但是工作中一般我们用于自定义定义线程池的名字,由于项目中存在很多线程池,定义特殊含义的名字可以帮助我们在生产中快速的查找日志,识别哪个线程池出了异常。

  private static ThreadPoolExecutor executor = new ThreadPoolExecutor(20, 100, 1,
      TimeUnit.MINUTES, new LinkedBlockingQueue<>(1000),
      new CustomizableThreadFactory("thread-demo-"),
      new ThreadPoolExecutor.CallerRunsPolicy()
  );

 
  

你可能感兴趣的:(技术专栏,java,spring,java-ee)