java线程池

一、使用线程池主要有以下三个原因

​ 1)、创建/销毁线程需要消耗系统资源,线程池可以复用已创建的线程

​ 2)、控制并发的数量。并发数量过多,可能会导致资源消耗过多,从而造成服务器崩溃。(主要原因)

​ 3)、可以对线程做统一管理

Java中的线程池顶层接口是Executor接口,ThreadPoolExecutor是这个接口的实现类。

二、ThreadPoolExecutor类 java.util.concurrent.ThreadPoolExecutor

/**
 * 构造方法 5个参数
 * @param corePoolSize  该线程池中核心线程数最大值
 * @param maximumPoolSize 该线程池中线程总数最大值
 * @param keepAliveTime 非核心线程闲置超时时长。
 * @param unit keepAliveTime的单位 TimeUnit.MILLISECONDS 毫秒
 * @param workQueue 阻塞队列,维护着等待执行的Runnable任务对象
 */
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
            Executors.defaultThreadFactory(), defaultHandler);
}

/**
 * 6个参数 6-1
 * @param threadFactory 创建线程工厂,设置一些默认参数
 */
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
            threadFactory, defaultHandler);
}

/**
 * 构造方法 6-2
 * @param handler 线程数量大于最大线程数就会采用拒绝处理策略  ThreadPoolExecutor.CallerRunsPolicy 由调用线程处理该任务
 */
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          RejectedExecutionHandler handler) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
            Executors.defaultThreadFactory(), handler);
}

/**
 * 构造方法 6-3  6-1和6-2的结合
 */
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;
}

​ 参数:

int corePoolSize:该线程池中核心线程数最大值

​ 核心线程:线程池中有两类线程,核心线程和非核心线程。核心线程默认情况下会一直存在于线程池中,而非核心线程如果长时间的闲置,就会被销毁。

int maximumPoolSize:该线程池中线程总数最大值

​ 该值等于核心线程数量 + 非核心线程数量。

long keepAliveTime非核心线程闲置超时时长

​ 非核心线程如果处于闲置状态超过该值,就会被销毁。如果设置allowCoreThreadTimeOut(true),则会也作用于核心线程。

TimeUnit unit:keepAliveTime的单位。

​ NANOSECONDS : 1微毫秒 = 1微秒 / 1000 MICROSECONDS : 1微秒 = 1毫秒 / 1000

​ MILLISECONDS : 1毫秒 = 1秒 /1000 SECONDS : 秒 MINUTES : 分 HOURS : 小时 DAYS : 天

BlockingQueue workQueue:阻塞队列,维护着等待执行的Runnable任务对象

几种常见的阻塞队列:

​ 1,LinkedBlockingQueue:链式阻塞队列,底层数据结构是链表,默认大小是Integer.MAX_VALUE,也可以指定大小。

​ 2,ArrayBlockingQueue:数组阻塞队列,底层数据结构是数组,需要指定队列的大小。

​ 3,SynchronousQueue:同步队列,内部容量为0,每个put操作必须等待一个take操作,反之亦然。

​ 4,DelayQueue:延迟队列,该队列中的元素只有当其指定的延迟时间到了,才能够从队列中获取到该元素 。

ThreadFactory threadFactory :创建线程的工厂,用于批量创建线程。

RejectedExecutionHandler handler拒绝处理策略,线程数量大于最大线程数就会采用拒绝处理策略。

​ ThreadPoolExecutor.AbortPolicy:默认拒绝处理策略,丢弃任务并抛出RejectedExecutionException异常。

​ ThreadPoolExecutor.DiscardPolicy:丢弃新来的任务,但是不抛出异常。

​ ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列头部(最旧的)的任务,然后重新尝试执行程序(如果再次失败,重复此过程)。

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

三、ThreadPoolExecutor的策略

线程池本身有一个调度线程,这个线程就是用于管理布控整个线程池里的各种任务和事务,例如创建线程、销毁线程、任务队列管理、线程队列管理等等。

故线程池也有自己的状态。ThreadPoolExecutor类中定义了一个volatile int变量runState来表示线程池的状态 ,分别为RUNNING、SHUTDOWN、STOP、TIDYING 、TERMINATED。

  • 线程池创建后处于RUNNING状态。

  • 调用shutdown()方法后处于SHUTDOWN状态,线程池不能接受新的任务,清除一些空闲worker,会等待阻塞队列的任务完成。

  • 调用shutdownNow()方法后处于STOP状态,线程池不能接受新的任务,中断所有线程,阻塞队列中没有被执行的任务全部丢弃。此时,poolsize=0,阻塞队列的size也为0。

  • 当所有的任务已终止,ctl记录的”任务数量”为0,线程池会变为TIDYING状态。接着会执行terminated()函数。

    ThreadPoolExecutor中有一个控制状态的属性叫ctl,它是一个AtomicInteger类型的变量。

  • 线程池处在TIDYING状态时,执行完terminated()方法之后,就会由 TIDYING -> TERMINATED, 线程池被设置为TERMINATED状态。

四、四种常见的线程池

Executors类中提供的四个静态方法来创建线程池

1、newCachedThreadPool

//创建一个核心线程数为0,线程总数为Integer.MAX_VALUE(2147483647),非核心线程60s超时,SynchronousQueue对列的线程池。创兼的只有非核心线程,线程闲置60s会被摧毁
//当需要执行很多短时间的任务时,CacheThreadPool的线程复用率比较高, 会显著的提高性能
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
            60L, TimeUnit.SECONDS,
            new SynchronousQueue<Runnable>());
}
public static void main(String[] args){
    ExecutorService executorService = Executors.newCachedThreadPool();
    for(int i = 0 ;i < 100;i ++){
        executorService.execute(() -> {
            System.out.println(Thread.currentThread().getName());
        });
    }
    //调用shutdown()方法后处于**SHUTDOWN**状态,线程池不能接受新的任务,清除一些空闲worker,会等待阻塞队列的任务完成。
    executorService.shutdown();
}

2、newFixedThreadPool

//创建一个核心线程数和线程总数一样,线程不会被摧毁,LinkedBlockingQueue对列的线程池。创建的只有核心线程。
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
            0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>());
}
public static void main(String[] args){
    ExecutorService executorService = Executors.newFixedThreadPool (5);
    for(int i = 0 ;i < 100;i ++){
        executorService.execute(() -> {
            System.out.println(Thread.currentThread().getName());
        });
    }
    //调用shutdown()方法后处于**SHUTDOWN**状态,线程池不能接受新的任务,清除一些空闲worker,会等待阻塞队列的任务完成。
    executorService.shutdown();
}

3、newSingleThreadExecutor

//创建一个核心线程和总线程数都为1,LinkedBlockingQueue对列的线程池。只有一个核心线程。
//任务按照先来先执行的顺序执行
public static ExecutorService newSingleThreadExecutor() {
    return new Executors.FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                    0L, TimeUnit.MILLISECONDS,
                    new LinkedBlockingQueue<Runnable>()));
}
public static void main(String[] args){
    ExecutorService executorService = Executors.newSingleThreadExecutor();
    for(int i = 0 ;i < 100;i ++){
        executorService.execute(() -> {
            System.out.println(Thread.currentThread().getName());
        });
    }
    //调用shutdown()方法后处于**SHUTDOWN**状态,线程池不能接受新的任务,清除一些空闲worker,会等待阻塞队列的任务完成。
    executorService.shutdown();
}

4、newScheduledThreadPool

//定长的线程池,支持周期性执行任务
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
            new ScheduledThreadPoolExecutor.DelayedWorkQueue());
}

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
            Executors.defaultThreadFactory(), defaultHandler);
}
 public static void main(String[] args){
        //支持周期的执行任务
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool (1);
     	// 2 第一次执行任务2s后执行,3任务之间的周期为3秒,TimeUnit.SECONDS 2和3的时间单位
        executorService.scheduleAtFixedRate(() ->{
            System.out.println(System.currentTimeMillis());
        },2,3, TimeUnit.SECONDS);
        //调用shutdown()方法后处于**SHUTDOWN**状态,线程池不能接受新的任务,清除一些空闲worker,会等待阻塞队列的任务完成。
        //executorService.shutdown();
 }

上一篇:Java 线程池 ThreadPoolExecutor 的使用


下一篇:室友的Zip加密文件探秘,Python解决Zip加密文件探索秘密!