线程池ThreadPoolExecutor的使用以及理解
来源:互联网 发布:mac 建筑 软件 收费吗 编辑:程序博客网 时间:2024/05/16 07:19
java线程池实现类ThreadPoolExecutor
ThreadPoolExecutor可以构造一个线程池,何为线程池,就是装了一堆线程的容器。这些线程可以用来执行任务。其实按道理讲,我们可以自己创建一个线程来执行自己的任务。那么为啥要用线程池呢,是因为线程池装了一堆已经创建好了的线程,不需要我们自己再去创建,这节省了创建线程的资源,也保障了线程的高效利用。
ThreadPoolExcecutor,Thread,Runable,Callable,Future的关系。
ThreadPoolExcecutor线程池中,装的是多个Thread。
Runable,Callable是两个可以交给线程执行的任务,它俩有个区别就是,Callable对应的call方法可以自定义返回值,Runable的run方法没有返回值。
那么Future在这其中又扮演什么角色呢?ThreadPoolExcecutor的submit方法,可以传入Callable任务,也可以传入Runable任务,该方法可以返回Future对象。Future对象可以获取任务的完成情况,Future的isDone()方法可以获取当前任务是否执行完毕,该方法不会阻塞;get()方法可以获取任务执行结果的返回值,注意,get()方法是会阻塞的方法,也就是说当前任务在线程中没有运行完毕,只要程序调用了get()方法,会一直阻塞,等到线程运行完毕之后,才能继续执行下去。
示例
以下示例中包含了ThreadPoolExcecutor、Callable、Future的相关方法的简单使用
陈旭运行结果请看代码后面
import java.util.ArrayList;import java.util.Arrays;import java.util.Iterator;import java.util.List;import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.Future;import java.util.concurrent.LinkedBlockingQueue;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;public class ExcutorTest { /** * 创建一个Callable任务,目的是:让当前线程睡眠一定时间 * * @param num * 睡眠时间 * @return */ public static Callable<Long> createCallable(final int num) { return new Callable<Long>() { @Override public Long call() throws Exception { Thread.sleep(num); return System.currentTimeMillis(); } }; } @SuppressWarnings("unchecked") public static void main(String[] args) { // 创建线程池 // 参数说明:核心线数,最大线程数,线程失效时间,线程失效时间的单位,等待线程池处理的任务 ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 3, 30, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()); long begin = System.currentTimeMillis();// 记录一下开始时间 try { // 往线程池放入5个Callable任务 Future<Long> f1 = threadPool.submit(createCallable(2 * 1000));// 此任务执行完毕需要消耗2秒 Future<Long> f2 = threadPool.submit(createCallable(3 * 1000));// 此任务执行完毕需要消耗3秒 Future<Long> f3 = threadPool.submit(createCallable(4 * 1000));// 此任务执行完毕需要消耗4秒 Future<Long> f4 = threadPool.submit(createCallable(5 * 1000));// 此任务执行完毕需要消耗5秒 Future<Long> f5 = threadPool.submit(createCallable(6 * 1000));// 此任务执行完毕需要消耗6秒 long startSucess = System.currentTimeMillis(); System.out.println("所有任务放入完毕:" + startSucess); Long f5Rst = f5.get(); // 如果下面打印有时间差,则证明get()方法会阻塞 System.out.println("\nf5任务返回数据的时间与所有任务放入完毕的时间差为:" + (System.currentTimeMillis() - startSucess)); System.out.println("\nf5任务返回的数据:" + f5Rst); System.out.println("\n获取完f5返回值的时间:" + System.currentTimeMillis()); Long f1Rst = f1.get(); Long f2Rst = f2.get(); Long f3Rst = f3.get(); Long f4Rst = f4.get(); // 如果当前时间和获取完f5返回值的时间相同,则证明剩下的几个任务已经在f5之前执行完毕。毕竟f5是耗时最长的任务 System.out.println("\n获取最早执行的任务,对比当前时间,看看会不会阻塞,当前时间为:" + System.currentTimeMillis()); System.out.println("\ntask count:" + threadPool.getTaskCount()); // 如何在有任务没执行完毕之前,保证程序一直阻塞在这儿呢? // 可以参考下面的代码 // 说明:因为有很多任务在执行或者等待执行,我们也不知道哪个任务耗时最长,所以直接调用Future的get()方法是不靠谱的 // 因此,我们可以把所有任务装在集合中,一直循环判断任务是否执行完毕(Future.isDone()),如果执行完毕,就从集合中移除 // 最后集合为空了,任务肯定就执行完毕了。这段代码解决了我自己遇到的一个问题,所以在这儿记录一下。 // 我看网上部分资料,使用了线程池的ThreadPoolExecutor.shutdown()方法,来保证所有任务执行完毕之前,一直阻塞在那儿 // 但是有一个问题是,调用了ThreadPoolExecutor.shutdown()之后,再往线程池中submit任务,就会抛异常,大家可以试试 // 这导致我的线程池就不能复用了,这太浪费资源了 List<Future<Long>> futures = new ArrayList<Future<Long>>(Arrays.asList(f1, f2, f3, f4, f5)); while (true) { Iterator<Future<Long>> iterator = futures.iterator();// 获取迭代器 while (iterator.hasNext()) { Future<Long> next = iterator.next(); if (next.isDone()) {// 如果当前任务完成 iterator.remove();// 则从list中移除 } } if (futures.isEmpty()) {// 如果list为空了,就证明所有任务都执行完毕了 break;//跳出此while循环,继续执行以下代码 } } } catch (InterruptedException e) { System.out.println("抛异常了,赶快处理"); } catch (ExecutionException e) { System.out.println("又抛异常了,赶快处理"); } long end = System.currentTimeMillis(); System.out.println("spend time:" + (end - begin)); System.out.println("处理完毕"); }}
运行结果
所有任务放入完毕:1506674591202
f5任务返回数据的时间与所有任务放入完毕的时间差为:12000
f5任务返回的数据:1506674603202
获取完f5返回值的时间:1506674603202
获取最早执行的任务,对比当前时间,看看会不会阻塞,当前时间为:1506674603202
task count:5
spend time:12001
处理完毕
分析及总结
可以从运行结果中明显看到Future的get()方法是会阻塞的。
另外,执行完毕的时间是12秒,任务耗时最长为6秒,那为啥为总的耗时是12秒呢?
因为呀,我们线程池初始化的时候,核心线程为2个,最大线程为3个,大家可以算一算。
- 线程池ThreadPoolExecutor的使用以及理解
- ThreadPoolExecutor线程池的使用与理解
- ThreadPoolExecutor线程池的使用与理解
- ThreadPoolExecutor线程池的使用
- 线程池ThreadPoolExecutor的使用
- ThreadPoolExecutor线程池的使用
- ThreadPoolExecutor线程池理解
- Java线程池ThreadPoolExecutor的简单理解
- 线程池ThreadPoolExecutor使用
- ThreadPoolExecutor线程池代码理解
- ThreadPoolExecutor线程池的简单使用
- 线程池类 ThreadPoolExecutor的使用
- ThreadPoolExecutor线程池的简单使用
- JAVA线程池ThreadPoolExecutor的简单使用
- java线程池ThreadPoolExecutor的使用
- java中ThreadPoolExecutor线程池的使用
- 线程池的使用(ThreadPoolExecutor详解)
- JAVA并发-ThreadPoolExecutor线程池的使用
- SourceTree解决冲突
- Android界面调试的基本方法---bitmap显示出来
- 事件总线框架EventBus和Otto学习笔记
- phpcmsv9 内容页调用{date('Y-m-d',time($inputtime))} 结果为1970-01-01.
- Linux目录结构
- 线程池ThreadPoolExecutor的使用以及理解
- 在github的readme添加图片
- 受限玻尔兹曼机
- python3应用openpyxl小结
- Chapter3 字符串、向量和数组
- SparkSQL(下)--Spark实战应用
- Python NLP入门教程
- nova的pause instance的log
- java基础---------字符串常量池-创建了几个对象