文章目录
描述
自定义线程池可以帮助我们掌握更多的线程自主权,方便于后期代码的维护和问题的排查。
应用知识点
- 自定义线程池
- 线程工厂
- 线程池关闭
- 线程池最佳线程数
在《Java Concurrency in Practice》一书中,给出了估算线程池大小的公式:
Nthreads = Ncpu x Ucpu x (1 + W/C),其中
Ncpu = CPU核心数
Ucpu = CPU使用率,0~1
W/C = 等待时间与计算时间的比率
public static void main(String [] args){
# 获取 CPU 数目
int CPU_numbers = Runtime.getRuntime().availableProcessors();
System.out.println("CPU_numbers="+CPU_numbers);
}
线程池阻塞队列
| 名称 | 底层实现 | 处理顺讯 | 添加顺序 | 容量 | 文档 |
|---|---|---|---|---|---|
| ArrayBlockingQueue | 数组 | FIFO | 新任务添加到队尾 | 容量可在初始化时指定 | |
| LinekdBlockingQueue | 链表 | FIFO | 新任务添加到队尾 | 默认 Integer.MAX_VALUE | |
| PriorityBlockingQueue | 数组 | 任务线程优先级 | 内部按照平衡二叉堆 | 理论上无界,默认为11,但是数组长度不能大于 Integer.MAX_VALUE - 8,否则主动报 OutOfMemoryError | https://www.jianshu.com/p/3ab47f9bdfe8 |
| DelayQueue | 数组 | 按照过期时间 | 无界 | ||
| SynchronousQueue | 结点 | 每一个put操作必须等待take操作,支持公平锁和非公平锁 | 无界 | ||
| LinkedBlockingDeque | 链表(双向) | FIFO/FILO | 默认 Integer.MAX_VALUE | ||
| LinkedTransferQueue | 链表 | FIFO | 无界,消费者预占模式,如果存在预占,新元素不入队,直接被消费者消费 |
向阻塞队列添加元素的方法 add\offer\put 的区别
| 类型 | 说明 | 方法定义 |
|---|---|---|
| BlockingQueue.add | 要没在队尾添加成功,如果队列已满,则直接抛出 IllegalStateException 异常 | boolean add(E e) |
| BlockingQueue.offer | 要没在队尾添加成功,如果队列已满,则直接返回 false | boolean offer(E e) |
| BlockingQueue.put | 要没在队尾添加成功,如果队列已满,等待直到可以获取一个元素 | void put(E e) throws InterruptedException |
从阻塞队列获取元素的方法 take\poll\remove
poll 支持等待一段时间
| 类型 | 说明 | 方法定义 |
|---|---|---|
| BlockingQueue.take | 从队首获取一个元素,如果没有元素则等待 | E take() throws InterruptedException |
| Queue.poll | 返回队首的一个元素,如果没有则返回null | E poll() |
| BlockingQueue.poll | 返回队首的一个元素,如果没有则等待一段时间,如果仍没有获取,则返回null | E poll(long timeout, TimeUnit unit) throws InterruptedException |
| BlockingQueue.remove(Object o) | 从队列中移除指定对象,如果成功返回true,如果不存在则返回 false | boolean remove(Object o); |
线程池拒绝策略
四种策略
| ThreadPoolExecutor.AbortPolicy() | 中止待被执行的线程,抛出 RejectedExecutionException | 默认饱和策略 |
| ThreadPoolExecutor.CallerRunsPolicy() | 不会占用线程池的线程,调用线程池的主线程参与该线程执行 | - |
| ThreadPoolExecutor.DiscardOldestPolicy() | 抛弃待最先被执行的任务,优先级越高越会被先抛弃 | - |
| DiscardPolicy() | 直接抛弃任务,不会抛出异常 | - |
默认策略
private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
核心线程数如何确保不被销毁
https://blog.youkuaiyun.com/smile_from_2015/article/details/105259789
代码
Maven
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>29.0-jre</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.72</version>
</dependency>
自定义 Java 线程池
- 支持 自定义线程工厂 namedThreadFactory
- 支持获取线程执行结果 newFutureTask
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.concurrent.*;
/**
* @Title: 线程池测试
* @Description: 线程池测试
* @Author: wujie
* @Version: v1.0
* @Date:2020-06-10
* @Updatedby:
*/
public class ThreadTest {
private final static int CORE_POOL_SIZE = 10;
private final static int MAX_I_MUM_POOL_SIZE = 10;
private final static long KEEP_ALIVE_TIME = 0L;
private final static int WORKQUEUE_SIZE = 1024;
/**
* 自定义线程名称,方便的出错的时候溯源
*/
private static ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("center-pool-%d").build();
private static ExecutorService threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE, MAX_I_MUM_POOL_SIZE, KEEP_ALIVE_TIME,
TimeUnit.MILLISECONDS, new LinkedBlockingDeque(WORKQUEUE_SIZE), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
//使用lamada表达式实现接口作为匿名内部类
private static ExecutorService threadPool2 = new ThreadPoolExecutor(CORE_POOL_SIZE, MAX_I_MUM_POOL_SIZE, KEEP_ALIVE_TIME,
TimeUnit.MILLISECONDS,
new LinkedBlockingDeque(WORKQUEUE_SIZE),
(runnable)->{Thread t= new Thread(runnable)
t.setName("center-pool-%d");
return t;}
, new ThreadPoolExecutor.AbortPolicy());
/**
* 获取线程池
*/
public static ExecutorService getThreadPool() {
return threadPool;
}
/**
* 获取线程池工厂
*/
public static ThreadFactory getThreadFactory(){
return namedThreadFactory;
}
/**
* 执行任务
* @param r
*/
public static void newTask(Runnable r) {
threadPool.execute(r);
}
/**
* 执行任务-需要获取线程执行结果
* @param r
*/
public static Future newFutureTask(Runnable r) {
return threadPool.submit(r);
}
/**
* 关闭线程池(如有在执行任务则等待)
*/
public static void destroyExecutorService(){
System.out.println("关闭线程池");
if(!threadPool.isShutdown()){
threadPool.shutdown();
}
}
/**
* 立即关闭线程池
*/
public static void destroyNowExecutorService(){
System.out.println("立即关闭线程池");
if(!threadPool.isShutdown()){
threadPool.shutdownNow();
}
}
/**
* 线程池是否关闭
* @return
*/
public static boolean isExecutorServiceDownNow(){
return threadPool.isShutdown();
}
}
自定义线程池的简单使用
自定义线程池结合 Thread
public class ThreadUseTest {
private static class MyThread extends Thread{
@Override
public void run() {
super.run();
System.out.println("自定义线程池,可以通过构造参数传参");
}
}
public static void main(String [] args){
ThreadTest.newTask(ThreadTest.getThreadFactory().newThread(new MyThread()));
ThreadTest.destroyExecutorService();
}
}
自定义线程池结合 Runnable
public class ThreadUseTest {
private static class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("自定义线程池,可以通过构造参数传参");
}
}
public static void main(String [] args){
ThreadTest.newTask(ThreadTest.getThreadFactory().newThread(new MyRunnable()));
ThreadTest.destroyExecutorService();
}
}
自定义线程池结合 Future (需要获取执行结果)
Futuretask 学习(如果了解 Future 可以直接看 “使用线程池执行 FutureTask”小标题,否则建议阅读全文)
参考资料
[1]、https://www.cnblogs.com/xhq1024/p/12125290.html
902

被折叠的 条评论
为什么被折叠?



