JUC系列线程池基础使用

Posted 顧棟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JUC系列线程池基础使用相关的知识,希望对你有一定的参考价值。

线程池基础使用

文章目录

一、核心组件和核心类

4个核心组件

  • 线程池管理器 用于创建和管理线程
  • 工作线程 线程池中执行具体任务的线程
  • 任务接口 用户定义工作线程的调度执行策略,只有线程实现了该接口,线程的任务才能够被线程池调度。
  • 任务队列 存放待处理的任务,新的任务加入队列,执行完成的任务会被队列移除。

java中的线程池通过Executor框架实现的核心类如下:

Executor、Executors 、ExecutorService、ThreadPoolExcutor、Callable、Future、 FutureTask

创建线程池的核心类ThreadPoolExcutor的核心方法

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) 
        ...
    

参数说明

  • corePoolSize:线程池中的核心线程数量

  • maximumPoolSize:线程池中最大线程数量

  • keepAliveTime:当线程数超过corePoolSize时,线程空闲存活时长

  • unit keepAliveTime:的时间单位

  • workQueue:任务队列,被提交但是尚未执行的任务的存放处

  • threadFactory:线程工厂,用户创建线程,一般默认

  • handler:拒绝策略,任务过多或者其他原因导致线程池无法处理任务的拒绝策略。

二、线程池的工作流程

核心线程池和非核心线程池

核心线程不会被销毁 而非核心线在空闲后超过keepaliveTime之后会被销毁。

三、线程池的拒绝策略

当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize时,如果还有任务到来就会采取任务拒绝策略。JDK内置的拒绝策略有4种。

ThreadPoolExecutor.AbortPolicy //默认,丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy //丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy //尝试丢弃队列最前面的任务,然后重新提交被当前的任务
ThreadPoolExecutor.CallerRunsPolicy //由调用线程(提交任务的线程)处理该任务 如果调用线程未关闭的话

自定义拒绝策略

以上的拒绝策略都实现了RejectedExecutionHandler接口,说明可以自己通过实现RejectedExecutionHandler来编写自己的拒绝策略。

四、6种常用的线程池

  • newCachedThreadPool 缓存线程池

在创建新线程时,如果有空闲的线程就重用它们。 使用场景在执行时间短的大量任务是可以使用。

  • newFixedThreadPool

创建一个固定线程数的线程池,并形成队列循环使用。

  • newScheduledThreadPool

可定时调度的线程池,在延迟时间后执行或者定期执行某个线程任务。

  • newSingleThreadExecutor

线程池中只有1个线程执行任务,在该线程出现停止或异常时,会启动一个新的线程进行工作。

  • newSingleScheduleThreadPool

此线程有点结合的意思,是一个只有一个线程的线程池,可用定时任务。

  • newWorkStealingPool

创建一个具有抢占式操作的线程池,stealing 翻译为抢断、窃取的意思,它实现的一个线程池和上面4种都不一样,用的是 ForkJoinPool 类。它是一个并行的线程池,参数中传入的是一个线程并发的数量,这里和之前就有很明显的区别,前面4种线程池都有核心线程数、最大线程数等等,而这就使用了一个并发线程数解决问题。从介绍中,还说明这个线程池不会保证任务的顺序执行,也就是 WorkStealing 的意思,抢占式的工作。

五、线程池的使用

线程池的创建

 ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                5,
                10,
                3,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(3),
                new ThreadPoolExecutor.AbortPolicy());

向线程池提交任务

threadPoolExecutor.execute(new WorkTask(task));

关闭线程池

threadPoolExecutor.shutdown();

ThreadPoolExecutorDemo示例

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolExecutorDemo 
    public static void main(String[] args) throws InterruptedException 
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                5,
                10,
                3,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(3),
                new ThreadPoolExecutor.AbortPolicy());

        for (int i = 1; i <= 10; i++) 
            String task = "task-" + i;
            System.out.println("[" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + "--" + Thread.currentThread().getName() + "] submit " + task);
            threadPoolExecutor.execute(new WorkTask(task));
        
        threadPoolExecutor.shutdown();
    


    static class WorkTask implements Runnable 

        private final Object name;

        WorkTask(String taskName) 
            this.name = taskName;
        

        @Override
        public void run() 

            System.out.println("[" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + "--" + Thread.currentThread().getName() + "] start .." + name);

            try 
                Thread.sleep(2000);
                System.out.println("[" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + "--" + Thread.currentThread().getName() + "] end .." + name);

             catch (Exception e) 
                e.printStackTrace();
            
        
    

执行结果

[17:45:34--main] submit task-1
[17:45:34--main] submit task-2
[17:45:34--main] submit task-3
[17:45:34--main] submit task-4
[17:45:34--main] submit task-5
[17:45:34--main] submit task-6
[17:45:34--main] submit task-7
[17:45:34--main] submit task-8
[17:45:34--main] submit task-9
[17:45:34--main] submit task-10
[17:45:34--pool-1-thread-1] start ..task-1
[17:45:34--pool-1-thread-3] start ..task-3
[17:45:34--pool-1-thread-2] start ..task-2
[17:45:34--pool-1-thread-4] start ..task-4
[17:45:34--pool-1-thread-5] start ..task-5
[17:45:34--pool-1-thread-6] start ..task-9
[17:45:34--pool-1-thread-7] start ..task-10
[17:45:36--pool-1-thread-3] end ..task-3
[17:45:36--pool-1-thread-2] end ..task-2
[17:45:36--pool-1-thread-1] end ..task-1
[17:45:36--pool-1-thread-2] start ..task-6
[17:45:36--pool-1-thread-4] end ..task-4
[17:45:36--pool-1-thread-1] start ..task-7
[17:45:36--pool-1-thread-6] end ..task-9
[17:45:36--pool-1-thread-5] end ..task-5
[17:45:36--pool-1-thread-7] end ..task-10
[17:45:36--pool-1-thread-3] start ..task-8
[17:45:38--pool-1-thread-2] end ..task-6
[17:45:38--pool-1-thread-3] end ..task-8
[17:45:38--pool-1-thread-1] end ..task-7

以上是关于JUC系列线程池基础使用的主要内容,如果未能解决你的问题,请参考以下文章

Java多线程系列--“JUC线程池”04之 线程池原理

Java多线程系列--“JUC线程池”02之 线程池原理

JUC--自定义线程池

JUC并发编程 共享模式之工具 ThreadPoolExecutor 多线程设计模式 -- 异步模式之工作线程(定义饥饿 & 解决饥饿 & 线程池创建多少线程数目合适)

Java - "JUC线程池" 架构

Java多线程系列--“JUC线程池”02之 线程池原理