JAVA线程池 之 Executors (一) 简介

1、背景java

  线程属于系统稀缺资源,在使用线程时,若是无限制建立线程,达到CPU高负荷时,会致使系统运行缓慢,更有甚者直接宕机。数组

在这样的基础上咱们但愿在使用线程时,竟可能使系统线程数处于一个可控范围,尽量实现线程的重用。并发

 

2、Executors 分析app

Executors 示例  DEMOide

  

/**
 * @author binH
 * @date 2018/01/24
 */
package org.lsnbin.thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadAnalyze {
    
    private    static ExecutorService fexecutor = Executors.newFixedThreadPool(20);
    
    public static void main(String[] args) {
        for (int i = 0; i < 20; i++) {
            fexecutor.execute(new ThreadAnalyze().new Task());
        }
    }
    
    private class Task implements Runnable{

        @Override
        public void run() {
            System.out.println("Thread name --> " + Thread.currentThread().getName());
        }
    }
}

  示例分析:高并发

  一、使用Executors初始化一个包含10个线程的线程池性能

  二、使用execute方法提交20个任务,打印线程名this

  三、负责执行任务的线程的生命周期交由Executors管理。spa

 

3、Executors 内部分析线程

  Executors是线程池的工厂类,内部基于ThreadPoolExecutor实现,对ThreadPoolExecutor进行封装,并提供相对应方法。

  newFixedThreadPool

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

初始化必定数量
的线程池,当corePoolSize=maximumPoolSize时,使用LinkedBlockingQueue做为阻塞队列,不过当线程池不使用时,也不会释放线程。

newCachedThreadPool

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
不初始化线程,在须要使用时建立线程,最大容许数为
Integer.MAX_VALUE,使用SynchronousQueue做为阻塞队列,当线程空闲时keepAliveTime后释放线程。
容易存在问题:在高并发状况下,一瞬间会建立大量的线程,会出现严重的性能问题。

  newSingleThreadExecutor

 public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
初始化只有一个线程的线程池,若是该线程因为异常结束,会从新建立一个新的线程。线程不过时,使用
LinkedBlockingQueue做为阻塞队列。惟一个线程能够保证任务的顺序执行。

  newScheduledThreadPool

 public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
初始化必定数量线程的线程池,线程池中的任务能够在指定的时间内周期性的执行所提交的任务。可用于按期同步。

  newWorkStealingPool  -- JDK1.8

 public static ExecutorService newWorkStealingPool(int parallelism) {
        return new ForkJoinPool
            (parallelism,
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }
建立持有足够线程的线程池来支持给定的并行级别,并经过使用多个队列来减小竞争,须要指定
parallelism并行参数,若是没指定,则默认CPU数量。

  ForkJoinPool:支持大任务分解成小任务的线程池,这是Java8新增线程池,一般配合ForkJoinTask接口的子类RecursiveAction或RecursiveTask使用。

 

4、ThreadPoolExecutor 分析

 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> 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

    线程池核心线程数。当线程池提交一个任务时,线程池就会建立一个新线程并执行任务,直到线程池内运行状态的线程数等于corePoolSize。

    当继续有任务提交时,会被放入阻塞队列中。使用 prestartCoreThread() 方法能够提早预建立并启动全部核心线程。

  二、maximumPoolSize

    线程池最大可用线程数。在线程数等于corePoolSize时,若是当前阻塞队列满了,继续提交任务时会建立新的线程并执行任务。前提时线程数小于maximumPoolSize。

  三、keepAliveTime

    线程池中线程空闲存活时间。默认状况下该状况只会在大于corePoolSize时有用。可是只要keepAliveTime值非0,能够经过allowallowCoreThreadTimeOut(boolean value)     方法将超时策略应用于核心线程。

  四、unit

    keepAliveTime存活时间的单位。

  五、workQueue

    用来保存等待被执行的任务的阻塞队列。JDK实现的队列有如下几种:

      ArrayBlockingQueue:基于数组结构的有界阻塞队列,按照FIFO ( 先进先出 ) 排序任务。

      LinkedBlockingQueue:基于链表结构的阻塞队列,按照FIFO ( 先进先出 ) 排序任务。效果比ArrayBlockingQueue好。

      DelayQueue:具备延时执行的无界阻塞队列。

      SynchronousQueue:一个不能存储任何元素的阻塞队列,每一个插入的操做必须等待另外一个相应的删除操做,不然插入则会一直处于阻塞状态。

                第二个线程必须等待前一个线程结束。

      PriorityBlockingQueue:具备 优先级的无界阻塞队列。

 

  六、threadFactory

    建立线程的工厂

    默认工厂实现:

  

 DefaultThreadFactory() {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                                  Thread.currentThread().getThreadGroup();
            namePrefix = "pool-" +
                          poolNumber.getAndIncrement() +
                         "-thread-";
        }

 

  七、handler

      线程池的饱和策略,用于线程数饱和与阻塞队列已满的状况。有任务继续提交时触发的处理逻辑。线程池提供了4种策略:

      AbortPolicy:直接跑出异常,默认策略。

      DiscardPolicy:直接丢弃任务,不处理。

      DiscardOldestPolicy:丢弃阻塞队列中靠前的旧的任务,执行新的任务。

      CallerRunsPolicy:用调用者的线程来执行任务。

相关文章
相关标签/搜索