当前位置:网站首页>线程池的使用
线程池的使用
2022-07-05 20:36:00 【菜鸟xiaowang】
1.线程池使用场景
java中经常需要用到多线程来处理一些业务,我们非常不建议单纯使用继承Thread或者实现Runnable接口的方式来创建线程,那样势必有创建及销毁线程耗费资源、线程上下文切换问题。同时创建过多的线程也可能引发资源耗尽的风险,这个时候引入线程池比较合理,方便线程任务的管理。java中涉及到线程池的相关类均在jdk1.5开始的java.util.concurrent包中,涉及到的几个核心类及接口包括:Executor、Executors、ExecutorService、ThreadPoolExecutor、FutureTask、Callable、Runnable等。
线程池可以:
加快请求响应(响应时间优先)
加快处理大任务(吞吐量优先)
使用过程
//创建线程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 10, 30, TimeUnit.SECONDS, new SynchronousQueue<>());
//向线程池提交任务 无返回值
threadPoolExecutor.execute(new Runnable() {
@Override
public void run() {
}
});
//向线程池提交任务 有返回值
Future<String> submit = threadPoolExecutor.submit(new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println("threadPoolExecutor submit");
return "null";
}
});
//返回任务的执行结果
try {
Object o = submit.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
//关闭线程池
threadPoolExecutor.shutdown();
线程池的创建
线程池可以自动创建也可以手动创建,
自动创建
自动创建体现在Executors工具类中,常见的可以创建newFixedThreadPool、newCachedThreadPool、newSingleThreadExecutor、newScheduledThreadPool;
//自动创建不同类型的线程池,使用Executors类,指定的参数比较少
//手动创建使用 ThreadPoolExecutor ,可以指定多个参数
//创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
scheduledExecutorService.schedule(new Runnable() {
@Override
public void run() {
System.out.println("延迟四秒执行");
}
},4, TimeUnit.SECONDS);
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("延迟1S后,每隔3秒执行一");
}
},1,3,TimeUnit.SECONDS);
//返回一个线程池(这个线程池只有一个线程),这个线程池可以在线程死后(或发生异常时)
// 重新启动一个线程来替代原来的线程继续执行下去
ExecutorService executorService = Executors.newSingleThreadExecutor();
//创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程
ExecutorService executorService1 = Executors.newFixedThreadPool(3);
//创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们
ExecutorService executorService2 = Executors.newCachedThreadPool();
手动创建
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 10, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardOldestPolicy());
手动创建体现在可以灵活设置线程池的各个参数,体现在代码中即ThreadPoolExecutor类构造器上各个实参的不同:
//手动创建线程池
/*
corePoolSize:核心线程数,也是线程池中常驻的线程数,线程池初始化时默认是没有线程的,当任务来临时才开始创建线程去执行任务
maximumPoolSize:最大线程数,在核心线程数的基础上可能会额外增加一些非核心线程,需要注意的是只有当workQueue队列填满时才会创建多于corePoolSize的线程(线程池总线程数不超过maxPoolSize)
keepAliveTime:非核心线程的空闲时间超过keepAliveTime就会被自动终止回收掉,注意当corePoolSize=maxPoolSize时,keepAliveTime参数也就不起作用了(因为不存在非核心线程);
unit:keepAliveTime的时间单位
workQueue:用于保存任务的队列,可以为无界、有界、同步移交new SynchronousQueue<>()三种队列类型之一,当池子里的工作线程数大于corePoolSize时,这时新进来的任务会被放到队列中
threadFactory:创建线程的工厂类,默认使用Executors.defaultThreadFactory(),也可以使用guava库的ThreadFactoryBuilder来创建
handler:线程池无法继续接收任务(队列已满且线程数达到maximunPoolSize)时的饱和策略,取值有AbortPolicy、CallerRunsPolicy、DiscardOldestPolicy、DiscardPolicy
*/
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 10, 30, TimeUnit.SECONDS, new SynchronousQueue<>());
workQueue队列
SynchronousQueue(同步移交队列):队列不作为任务的缓冲方式,可以简单理解为队列长度为零
LinkedBlockingQueue(无界队列):队列长度不受限制,当请求越来越多时(任务处理速度跟不上任务提交速度造成请求堆积)可能导致内存占用过多或OOM
ArrayBlockintQueue(有界队列):队列长度受限,当队列满了就需要创建多余的线程来执行任务
handler拒绝策略
- AbortPolicy:中断抛出异常
- DiscardPolicy:默默丢弃任务,不进行任何通知
- DiscardOldestPolicy:丢弃掉在队列中存在时间最久的任务
- CallerRunsPolicy:让提交任务的线程去执行任务(对比前三种比较友好一丢丢)
关闭线程池
shutdownNow():立即关闭线程池(暴力),正在执行中的及队列中的任务会被中断,同时该方法会返回被中断的队列中的任务列表
shutdown():平滑关闭线程池,正在执行中的及队列中的任务能执行完成,后续进来的任务会被执行拒绝策略
isTerminated():当正在执行的任务及对列中的任务全部都执行(清空)完就会返回true
线程池线程复用原理
1.线程池里执行的是任务,核心逻辑在ThreadPoolExecutor类的execute方法中,同时ThreadPoolExecutor中维护了HashSet<Worker> workers;
2.addWorker()方法来创建线程执行任务,如果是核心线程的任务,会赋值给Worker的firstTask属性;
3.Worker实现了Runnable,本质上也是任务,核心在run()方法里;
4.run()方法的执行核心runWorker(),自旋拿任务while (task != null || (task = getTask()) != null)),task是核心线程Worker的firstTask或者getTask();
5.getTask()的核心逻辑:
1.若当前工作线程数量大于核心线程数->说明此线程是非核心工作线程,通过poll()拿任务,未拿到任务即getTask()返回null,然后会在processWorkerExit(w, completedAbruptly)方法释放掉这个非核心工作线程的引用;
2.若当前工作线程数量小于核心线程数->说明此时线程是核心工作线程,通过take()拿任务
3.take()方式取任务,如果队列中没有任务了会调用await()阻塞当前线程,直到新任务到来,所以核心工作线程不会被回收; 当执行execute方法里的workQueue.offer(command)时会调用Condition.singal()方法唤醒一个之前阻塞的线程,这样核心线程即可复用
Callable和Runnable
Runnable和Callable都可以理解为任务,里面封装着任务的具体逻辑,提交给线程池执行,区别在于Runnable任务执行没有返回值,且Runnable任务逻辑中不能通过throws抛出cheched异常(但是可以try catch),而Callable可以获取到任务的执行结果返回值且抛出checked异常。
Future和FutureTask
Future接口用来表示执行异步任务的结果存储器,当一个任务的执行时间过长就可以采用这种方式:把任务提交给子线程去处理,主线程不用同步等待,当向线程池提交了一个Callable或Runnable任务时就会返回Future,用Future可以获取任务执行的返回结果。
Future的主要方法包括:
get()方法:返回任务的执行结果,若任务还未执行完,则会一直阻塞直到完成为止,如果执行过程中发生异常,则抛出异常,但是主线程是感知不到并且不受影响的,除非调用get()方法进行获取结果则会抛出ExecutionException异常;
get(long timeout, TimeUnit unit):在指定时间内返回任务的执行结果,超时未返回会抛出TimeoutException,这个时候需要显式的取消任务;
cancel(boolean mayInterruptIfRunning):取消任务,boolean类型入参表示如果任务正在运行中是否强制中断;
isDone():判断任务是否执行完毕,执行完毕不代表任务一定成功执行,比如任务执行失败但也执行完毕、任务被中断了也执行完毕都会返回true,它仅仅表示一种状态说后面任务不会再执行了;
isCancelled():判断任务是否被取消;
边栏推荐
- CVPR 2022 | common 3D damage and data enhancement
- Where is a good stock account? Is online account manager safe to open an account
- 小程序项目结构
- go 文件路径操作
- Minimum commission for stock trading account opening, where to open an account with low commission? Is it safe to open an account on your mobile phone
- 计算lnx的一种方式
- Leetcode (695) - the largest area of an island
- Simple understanding of interpolation search
- sort和投影
- Applet event binding
猜你喜欢
[quick start to digital IC Verification] 8. Typical circuits in digital ICs and their corresponding Verilog description methods
Unity editor extended UI control
Leetcode skimming: binary tree 16 (path sum)
Welcome to the game and win rich bonuses: Code Golf Challenge officially launched
2.8、项目管理过程基础知识
2022北京眼睛健康用品展,护眼产品展,中国眼博会11月举办
Frequent MySQL operations cause table locking problems
AI 从代码中自动生成注释文档
Applet page navigation
Leetcode brush question: binary tree 13 (the same tree)
随机推荐
Applet project structure
1. Strengthen learning basic knowledge points
[quick start of Digital IC Verification] 1. Talk about Digital IC Verification, understand the contents of the column, and clarify the learning objectives
Codeforces Round #804 (Div. 2) - A, B, C
ByteDance dev better technology salon was successfully held, and we joined hands with Huatai to share our experience in improving the efficiency of web research and development
Leetcode skimming: binary tree 16 (path sum)
ProSci LAG-3 重组蛋白说明书
Document method
document方法
无卷积骨干网络:金字塔Transformer,提升目标检测/分割等任务精度(附源代码)...
Minimum commission for stock trading account opening, where to open an account with low commission? Is it safe to open an account on your mobile phone
Rainbond 5.7.1 支持对接多家公有云和集群异常报警
Nprogress plug-in progress bar
科普|英语不好对NPDP考试有影响吗 ?
Abbkine BCA法 蛋白质定量试剂盒说明书
Where is a good stock account? Is online account manager safe to open an account
Station B up builds the world's first pure red stone neural network, pornographic detection based on deep learning action recognition, Chen Tianqi's course progress of machine science compilation MLC,
Which securities is better for securities account opening? Is online account opening safe?
Usaco3.4 "broken Gong rock" band raucous rockers - DP
常用的视图容器类组件