ThreadPoolExecutor 简介

ThreadPoolExecutor 是 java.util.concurrent 包下的一个类,在jdk1.5版本引入,帮助开发人员管理线程并方便地执行并行任务。

通俗来说,ThreadPoolExecutor 的作用是生产和管理线程池的,可以通过调用其 execute 方法和 submit 方法执行多线程任务。

ThreadPoolExecutor 使用

创建执行器

ExecutorService 对象和 ThreadPoolExecutor 的关系如下图:

ExecutorServiceConfig:

package com.ramble.threadpool.config;import java.util.concurrent.*;

public class ExecutorServiceConfig {

    /**
     * 定义一个并发任务执行器服务
     */
    private static ExecutorService executorService;
    /**
     * 在类加载的时候初始化并发任务执行器
     */
    static {
        init();
    }     /**
     * 防止类属性被篡改
     */
    private ExecutorServiceConfig() {
    }     /**
     * 初始化并发任务执行器。核心线程数量:设置为2,初始创建的线程池大小;最大线程数量:设置为3;空闲线程存活时间:设置为3秒,当非核心线程执行完任务之后,若没有新的任务分派,存活多久后自动销毁;任务队列:设置为2,当线程池创建的线程数量达到最大线程数量后,新进来的任务会排队等候;
     * 拒绝策略:设置为直接抛异常
     * <p>
     * 以上配置需要根据:实际的业务场景、项目实际情况、实际硬件情况等各种因素综合考量
     */
    private static void init() {
        executorService = new ThreadPoolExecutor(2, 3, 3, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(2), new ThreadPoolExecutor.AbortPolicy());
    }     /**
     * 获取默认并发任务执行器
     *
     * @return
     */
    public static ExecutorService getDefaultExecutor() {
        return executorService;
    }     /**
     * 获取固定大小并发任务执行器
     *
     * @return
     */
    public static ExecutorService getFixedExecutor() {
        return Executors.newFixedThreadPool(10);
    }     /**
     * 获取其他并发任务执行器
     * @return
     */
    public static ExecutorService getOtherExecutor() {
        //todo
        return null;
    }
}
  • 这个类的核心目的是构造一个 ExecutorService , 供业务代码调用。当业务代码需要创建线程执行任务的时候,不用创建、管理和维护“执行者(线程)”,只需要告诉 ExecutorService 需要做什么 “事情(任务)”。

  • ExecutorService 对象必须是单例的,因为此对象本身是管理线程池的,如果自己都不是线程安全的,那使用起来将有可能发生灾难。

  • 一个java进程允许创建多个ExecutorService 。根据业务实际情况,如果业务逻辑比较单一,大概率创建一种就满足使用。若业务繁杂,可根据业务特性抽象出多种类型以满足不同需求。譬如代码中就创建了两个 getDefaultExecutor 和 getFixedExecutor

  • 关于线程池配置,如核心线程数量、最大线程数量、拒绝策略等等没有绝对正确的值做参考,需要根据实际情况设定

创建任务

  • 通过实现 Runnable 接口并重写 run 方法创建无返回值的多线程任务
  • 通过实现 Callable 接口并重写 call 方法创建有返回值的多线程任务

Task1:


package com.ramble.threadpool.task;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class Task1 implements Runnable {
    /**
     * 新线程执行一个任务,没有参数,不需要返回值,此任务和其他任务没有先后顺序
     */
    @Override
    public void run() {
        try {
            Thread.sleep(3000L);
            log.info("TaskOne,thread is ={},thread name is={}", Thread.currentThread().getId(), Thread.currentThread().getName());
            throw new IllegalAccessException("主动抛一个异常");
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

Task2:


package com.ramble.threadpool.task;
import com.alibaba.fastjson.JSON;
import com.ramble.threadpool.dto.TaskTwoParam;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class Task2 implements Runnable {
    private TaskTwoParam param;
    public Task2(TaskTwoParam param) {
        this.param = param;
    }
    /**
     * 新线程执行一个任务,参数通过构造函数传递,不需要返回值,此任务和其他任务没有先后顺序
     */
    @Override
    public void run() {
        try {
            Thread.sleep(3000L);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        log.info("TaskTwo,thread  id is ={},thread name is={}, param is ={}", Thread.currentThread().getId(), Thread.currentThread().getName(), JSON.toJSONString(param));
    }
}

Task3:


package com.ramble.threadpool.task;
import com.alibaba.fastjson.JSON;
import com.ramble.threadpool.dto.TaskDto;
import com.ramble.threadpool.dto.TaskTwoParam;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.Callable;
@Slf4j
public class Task3 implements Callable {
    private TaskTwoParam param;
    public Task3(TaskTwoParam param) {
        this.param = param;
    }
    /**
     * 新线程执行一个任务,通过构造函数传递参数,有返回值,此任务和其他任务没有先后顺序
     *
     * @return
     * @throws Exception
     */
    @Override
    public Object call() throws Exception {
        log.info("Task3,thread  id is ={},thread name is={}, param is ={}", Thread.currentThread().getId(), Thread.currentThread().getName(), JSON.toJSONString(param));
        return new TaskDto().setName("task3-callable");
    }
}

Task4:


package com.ramble.threadpool.task;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class Task4 implements Runnable {
    /**
     * 新线程执行一个任务,没有参数,不需要返回值,此任务和其他任务没有先后顺序
     */
    @Override
    public void run() {
        try {
            Thread.sleep(3000L);
            log.info("Task4,thread is ={},thread name is={}", Thread.currentThread().getId(), Thread.currentThread().getName());
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

执行任务

在 controller 中模拟发起多线程任务。


package com.ramble.threadpool.controller;
import com.alibaba.fastjson.JSON;
import com.ramble.threadpool.config.ExecutorServiceConfig;
import com.ramble.threadpool.dto.TaskDto;
import com.ramble.threadpool.dto.TaskTwoParam;
import com.ramble.threadpool.task.Task1;
import com.ramble.threadpool.task.Task2;
import com.ramble.threadpool.task.Task3;
import com.ramble.threadpool.task.Task4;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.task.TaskExecutionProperties;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
   
   
   
    @GetMapping("/task")
    public String testTask() {
        log.info("testTask,thread is ={},thread name is={}", Thread.currentThread().getId(), Thread.currentThread().getName());
//        for (int i = 0; i < 4; i++) {
//            ExecutorServiceConfig.getExecutor().execute(new Task1());
//        }
//
//        for (int i = 0; i < 5; i++) {
//            ExecutorServiceConfig.getExecutor().execute(new Task2(new TaskTwoParam().setId(100).setName("cnaylor")));
//        }
        ExecutorServiceConfig.getDefaultExecutor().execute(new Task1());
        ExecutorServiceConfig.getDefaultExecutor().execute(new Task2(new TaskTwoParam().setId(100).setName("cnaylor")));
        ExecutorServiceConfig.getFixedExecutor().execute(new Task4());
        Future<TaskDto> taskResult = ExecutorServiceConfig.getDefaultExecutor().submit(new Task3(new TaskTwoParam().setId(100).setName("cnaylor")));
        TaskDto taskDto;
        try {
            taskDto = taskResult.get();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }
        return "testTask";
    }

ThreadPoolExecutor 拒绝策略

所谓拒绝策略,一般是指当前线程池处于满负荷状态,所有的线程都有正在处理的任务,阻塞队列也排满了的情况下,对新进来的任务做出何种响应。

默认的拒绝策略有四种,同时也可以自定义拒绝策略。

  • AbortPolicy
  • CallerRunsPolicy
  • DiscardOldestPolicy
  • DiscardPolicy

AbortPolicy

抛一个异常(RejectedExecutionException),此任务将无法执行。

CallerRunsPolicy

线程池满载了, 此任务将由调用发起线程来执行。比如我们在一个http请求线程中调用了线程池处理异步任务,但是现在线程池满了,那么此任务将转由http请求 线程处理。缺点是会导致http请求线程阻塞,达不到异步处理的效果。优点是任务会正常执行,避免被任务执行器丢弃。

DiscardOldestPolicy

在阻塞队列最前端抛弃一个任务,然后将此任务添加到阻塞队列中排队。最前端是指最先添加到阻塞队列的任务。

DiscardPolicy

当前任务不会执行,也不会抛异常,好像什么也没有发生一样。

自定义拒绝策略

自己编写一个类,实现 RejectedExecutionHandler 接口,并重写 rejectedExecution 方法即可实现自定义拒绝策略。需要在实例化 ThreadPoolExecutor 的时候,将自定义策略配置进去。

Java线程池ThreadPoolExecutor极简教程的更多相关文章

  1. Java线程池ThreadPoolExecutor使用和分析(三) - 终止线程池原理

    相关文章目录: Java线程池ThreadPoolExecutor使用和分析(一) Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理 Java线程池Thr ...

  2. Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理

    相关文章目录: Java线程池ThreadPoolExecutor使用和分析(一) Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理 Java线程池Thr ...

  3. Java线程池ThreadPoolExecutor使用和分析(一)

    相关文章目录: Java线程池ThreadPoolExecutor使用和分析(一) Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理 Java线程池Thr ...

  4. Java线程池ThreadPoolExecutor类源码分析

    前面我们在java线程池ThreadPoolExecutor类使用详解中对ThreadPoolExector线程池类的使用进行了详细阐述,这篇文章我们对其具体的源码进行一下分析和总结: 首先我们看下T ...

  5. java线程池ThreadPoolExecutor使用简介

    一.简介线程池类为 java.util.concurrent.ThreadPoolExecutor,常用构造方法为:ThreadPoolExecutor(int corePoolSize, int m ...

  6. Java 线程池(ThreadPoolExecutor)原理分析与使用

    在我们的开发中"池"的概念并不罕见,有数据库连接池.线程池.对象池.常量池等等.下面我们主要针对线程池来一步一步揭开线程池的面纱. 使用线程池的好处 1.降低资源消耗 可以重复利用 ...

  7. Java 线程池(ThreadPoolExecutor)原理解析

    在我们的开发中“池”的概念并不罕见,有数据库连接池.线程池.对象池.常量池等等.下面我们主要针对线程池来一步一步揭开线程池的面纱. 有关java线程技术文章还可以推荐阅读:<关于java多线程w ...

  8. Java线程池(ThreadPoolExecutor)原理分析与使用

    在我们的开发中"池"的概念并不罕见,有数据库连接池.线程池.对象池.常量池等等.下面我们主要针对线程池来一步一步揭开线程池的面纱. 使用线程池的好处 1.降低资源消耗 可以重复利用 ...

  9. 转:JAVA线程池ThreadPoolExecutor与阻塞队列BlockingQueue

    从Java5开始,Java提供了自己的线程池.每次只执行指定数量的线程,java.util.concurrent.ThreadPoolExecutor 就是这样的线程池.以下是我的学习过程. 首先是构 ...

随机推荐

  1. 小记-用canvas完成图像液化(向前变形)过程

    前几天由于团队需要,折腾了一下图像液化的处理过程. 现在来整理一下思路,做个记录. 用到公式如下,网上拿来的 话不多说,上代码 本来想尽量写出点逼格...后来发现怎么写也还是几个function搞定, ...

  2. CSS3新特性的概述

    CSS3的新特性大致分为以下六类 1.CSS3选择器 2.CSS3边框与圆角 3.CSS3背景与渐变 4.CSS3过渡 5.CSS3变换 6.CSS3动画 下面分别说一说以上六类都有哪些内容 CSS3 ...

  3. C#编写一个简易的文件管理器

    编写一个简易的文件管理器,通过本次实验,练习 TreeView.ListView 和SplitContainer 控件的使用,同时熟悉 C#文件系统的操作方法以及 File 类和 Directory类 ...

  4. PAT B1002写出这个数

    读入一个正整数 n,计算其各位数字之和,用汉语拼音写出和的每一位数字. 输入格式: 每个测试输入包含 1 个测试用例,即给出自然数 n 的值.这里保证 n 小于 1. 输出格式: 在一行内输出 n 的 ...

  5. (ICONIP2021)On the Unreasonable Effectiveness of Centroids in Image

    目录 摘要 1.引言 2.提出的方法 2.1 CentroidTripletloss 2.2 聚合表示 3.实验 3.1 数据集 3.2 应用细节 3.3 Fashion检索结果 3.4 行人再识别结 ...

  6. CommonsCollection7反序列化链学习

    CommonsCollections7 1.前置知识 Hashtable Hashtable实现了Map接口和Serializable接口,因此,Hashtable现在集成到了集合框架中.它和Hash ...

  7. 讲解CPU之NUMA硬件体系以及机制(lscpu查看相关信息)

    先看看从系统层面反映出来的numa cpu信息.采样机器为实体机.80核.128内存. [root@ht2 src]# lscpu Architecture: x86_64 #x86架构下的64位 C ...

  8. linux压缩打包、定时任务

    压缩打包 gzip压缩 win中的压缩包:zip rar Linux常见的压缩包有哪些? gzip bzip2 1.gzip压缩 压缩命令:gzip [压缩文件] 解压命令:gzip -d [压缩包] ...

  9. Unity—2D边缘检测(描边效果)

    一.ShaderLab 1.Alpha值边缘检测 根据图片的Alpha值边缘判定,向内扩一段距离做边缘,颜色设置未描边颜色: 片元着色阶段,向上下左右四个方向做检测,有一个点的透明度为0,判定为边缘: ...

  10. Java学习day16

    IO流即输入/输出流,按数据类型分为:字节流和字符流 与IO有关的操作最后都要释放,使用close方法 以字节流形式写入数据后需要换行可以添加换行符,注意旧版系统之间识别的换行符不相同,旧版Windo ...