Java 自定义线程池的线程工厂
本文分享创建线程工厂 ThreadFactory 的三种方式,以方便大家快速创建线程池,并通过线程工厂给每个创建出来的线程设置极富业务含义的名字。
线程池大小考虑因素
由于需要自定义线程池,故这里先介绍线程池大小如何设定最为合理。我们需要分析计算环境、资源预算和任务的特性,例如,考虑一下在部署的服务器上有多少个CPU,内存是多大,任务是计算密集型、I/O密集型还是二者皆可。
对于计算密集型的任务,在拥有N(CPU)个处理器的服务器上,当线程池的大小为N+1时,通常能实现最优的利用率。对于包含I/O操作或者其它阻塞操作的任务,由于线程并不会一直执行,因此线程池的规模应该更大,参考值可以设置为2**N(CPU)。线程池资源并不是唯一影响线程池大小的资源,还包括内存、文件句柄、套接字句柄和数据库连接等。在观察任务运行情况和系统负载、资源利用率之后,请酌情调整。
在Java中,可以使用如下代码获取CPU的数量:
int N(CPU)= Runtime.getRuntime().availableProcessors();
基于此可以设置合适的线程池数量,提高系统稳定性和吞吐率。
Spring CustomizableThreadFactory
此方式是利用Spring 框架提供的轮子 CustomizableThreadFactory。
ThreadFactory springFactory = new CustomizableThreadFactory("spring-pool-");
ExecutorService exec = new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(100), springFactory);
exec.submit(() -> {
logger.info("--记忆中的颜色是什么颜色---");
});
guava ThreadFactoryBuilder
使用Google 开源框架guava提供的 ThreadFactoryBuilder 可以快速给线程池里的线程设置有意义的名字。
ThreadFactory guavaFactory = new ThreadFactoryBuilder().setNameFormat("guava-pool-").build();
ExecutorService exec = new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(100),guavaFactory );
exec.submit(() -> {
logger.info("--记忆中的颜色是什么颜色---");
});
此方法需要引入如下guava maven坐标:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>
Apache commons-lang3 BasicThreadFactory
Apache commons-lang3 提供的 BasicThreadFactory.
ThreadFactory basicFactory = new BasicThreadFactory.Builder()
.namingPattern("basicFactory-pool-").build();
ExecutorService exec = new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(100),basicFactory );
exec.submit(() -> {
logger.info("--记忆中的颜色是什么颜色---");
});
使用的列队是无界队列LinkedBlockingQueue。如果当前执行任务数量大于核心线程数,此时再提交任务就在添加到阻塞队列中等待执行,直到有可用线程。温馨提示,无界阻塞队列可能消耗很大的内存资源。值得注意的是,如果使用了无界的任务队列,线程池最大数量这个参数就没什么效果了。
优雅的自定义线程工厂
除了使用第三方jar包之外,我们也可以基于ThreadPoolExecutor优雅地自定义ThreadFactory,并根据自己的需要来操作线程,下面是实例代码:
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.*;
@Slf4j
public class ThreadPoolStudy {
public static void main(String[] args) {
ExecutorService executor = initExecutor();
// 若i>0,则不会抛出异常信息。设为0是为了验证如何捕捉异常
for (int i = 0; i < 10; i++) {
DivTask myTask = new DivTask(100, i);
Future future = executor.submit(myTask);
try {
// 阻塞方法,尽量不要调用,这里调用get是为了避免任务执行异常被吞掉
future.get();
} catch (Exception e) {
log.error("任务执行异常,", e);
}
}
executor.shutdown();
log.info("线程池马上关闭,不再接收新任务。");
}
// 自定义任务,用于求两数相除的结果
class DivTask implements Runnable {
int a, b;
public DivTask(int a, int b) {
this.a = a;
this.b = b;
}
@Override
public void run() {
log.info("计算结果:{}", a / b);
}
}
/**
* 初始化线程池
*
* @return
*/
private static ExecutorService initExecutor() {
ThreadFactory guavaFactory = new ThreadFactoryBuilder().setNameFormat("guava-pool-").build();
ExecutorService executor = new ThreadPoolExecutor(1, 64,
30L, TimeUnit.MILLISECONDS,
new SynchronousQueue<Runnable>(), guavaFactory);
return executor;
}
}
任务执行完毕后,请关闭线程池,及时降低资源损耗。
小结
作为程序员,要有“刨根问底”的精神。知其然,更要知其所以然。这篇文章希望能帮助你对自定义线程工厂抽丝剥茧,还原背后的原理。
Reference
Java 自定义线程池的线程工厂的更多相关文章
- 由浅入深理解Java线程池及线程池的如何使用
前言 多线程的异步执行方式,虽然能够最大限度发挥多核计算机的计算能力,但是如果不加控制,反而会对系统造成负担.线程本身也要占用内存空间,大量的线程会占用内存资源并且可能会导致Out of Memory ...
- -1-5 java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁 sleep()和wait()方法的区别 为什么wait(),notify(),notifyAll()等方法都定义在Object类中
本文关键词: java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁 sleep()和wait()方法的区别 为什么wait( ...
- Java多线程、线程池和线程安全整理
多线程 1.1 多线程介绍 进程指正在运行的程序.确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能. 1.2 Thread类 通 ...
- Java多线程系列 JUC线程池02 线程池原理解析(一)
转载 http://www.cnblogs.com/skywang12345/p/3509960.html ; http://www.cnblogs.com/skywang12345/p/35099 ...
- 适配器、工厂模式、线程池、线程组、互斥锁、Timer类、Runtime类、单例设计模式(二十四)
1.多线程方法 * Thread 里面的俩个方法* 1.yield让出CPU,又称为礼让线程* 2.setPriority()设置线程的优先级 * 优先级最大是10,Thread.MAX_PRIORI ...
- java 线程池(线程的复用)
一. 线程池简介 1. 线程池的概念: 线程池就是首先创建一些线程,它们的集合称为线程池.使用线程池可以很好地提高性能,线程池在系统启动时即创建大量空闲的线程,程序将一个任务传给线程池,线程池就会启动 ...
- Java多线程系列 JUC线程池03 线程池原理解析(二)
转载 http://www.cnblogs.com/skywang12345/p/3509954.html http://www.cnblogs.com/skywang12345/p/351294 ...
- Java多线程系列 JUC线程池01 线程池框架
转载 http://www.cnblogs.com/skywang12345/p/3509903.html 为什么引入Executor线程池框架 new Thread()的缺点 1. 每次new T ...
- Java多线程系列 JUC线程池07 线程池原理解析(六)
关闭“线程池” shutdown()的源码如下: public void shutdown() { final ReentrantLock mainLock = this.mainLock; // ...
- 多线程、线程池、线程创建、Thread
转载自https://www.cnblogs.com/jmsjh/p/7762034.html 多线程 1.1 多线程介绍 学习多线程之前,我们先要了解几个关于多线程有关的概念. 进程:进程指正在运行 ...
随机推荐
- 第二课 - 输入(按键)控制输出(LED)-设备树
在第一课中学习了如何安装NCS开发环境,以及如何新建一个工程,还有如何构建和下载到开发板.并运行了官方的LED闪烁例程. 设备树 我们继续跟着官方开发者学院的教程来学习第二课的课程.官方课程包含了以下 ...
- Matlab转python的索引问题
python 中numpy库可以实现类似matlab多维数组的运算.但两者在索引方式上存在一些差异.这是需要注意的.例如: % 定义一个4*4矩阵 A=1:16; A=reshape(A,[4,4]) ...
- python excel 打开表格:表格名不知道应该怎么打开
取所有表格名的倒数第一个就是操作的表格 import pandas as pd xl = pd.ExcelFile(xlPath) names = xl.sheet_names df = xl.par ...
- docker批量删除容器或镜像
删除容器 停止所有容器 删除所有容器,需要先停止所有运行中的容器 docker stop `docker ps -a -q` docker ps -a -q,意思是列出所有容器(包括未运行的),只显示 ...
- Golang 入门 : 变量声明
变量声明 在Go中,变量是包含值的一块存储.可以使用变量声明为变量命名.只需使用var关键字,后跟所需的名称以及变量将保存的值的类型. 一旦你声明了一个变量,就可以用=为它分配该类型的任何值: qua ...
- Trae 开发工具与使用技巧
大家好,我是 V 哥. 前不久,字节重磅推出 AI 原生 IDE Trae,有了这款工具,程序员的开发效率得到了大大的提升,如何你是程序员,还没有使用起来,那 V 哥建议你即刻起马上安装上,谁用谁知道 ...
- Django实战项目-学习任务系统-任务管理
接着上期代码框架,开发第3个功能,任务管理,再增加一个学习任务表,用来记录发布的学习任务的标题和内容,预计完成天数,奖励积分和任务状态等信息. 第一步:编写第三个功能-任务管理 1,编辑模型文件: . ...
- 一文速通Python并行计算:01 Python多线程编程-基本概念、切换流程、GIL锁机制和生产者与消费者模型
一文速通 Python 并行计算:01 Python 多线程编程-基本概念.切换流程.GIL 锁机制和生产者与消费者模型 摘要: 多线程允许程序同时执行多个任务,提升效率和响应性.线程分为新建.就绪. ...
- 网络编程-关闭连接(1)-C/C++相关系统调用
背景 在linux网络编程中,经常需要编写关闭socket的代码,比如心跳检测失败需要关闭重连:网络报异常需要关闭重连.但究竟关闭操作做了什么,却不太清楚.目前项目使用Netty框架来实现的网络编程, ...
- 自定义的 Vue 3 Composition API 钩子,antd标签
1. 创建自定义钩子 useDeviceStatus.js: import { computed } from 'vue'; export function useDeviceStatus(statu ...