文档章节

(十七)java多线程之ThreadPoolExecutor

天之妖星
 天之妖星
发布于 2017/05/08 14:24
字数 2124
阅读 12
收藏 0

本人邮箱: kco1989@qq.com
欢迎转载,转载请注明网址 http://blog.csdn.net/tianshi_kco
github: https://github.com/kco1989/kco
代码已经全部托管github有需要的同学自行下载

引言

在之前的例子,我们要创建多个线程处理一批任务的时候.我是通过创建线程数组,或者使用线程集合来管理的.但是这样做不太好,因为这些线程没有被重复利用.所以这里要引入线程池,今天我们就讲线程池执行器ThreadPoolExecutor.

理论

首先我们来看一下它的构造器:
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) 这个是ThreadPoolExecutor完整的构造器,其他的构造器其实也是在内部调用这个.
* corePoolSize 核心线程数,线程池保留线程的数量,即使这些线程是空闲.除非设置了allowCoreThreadTimeOut
* maximumPoolSize 线程池最大允许的线程数.
* keepAliveTime 当当前的线程数大于核心线程数,那么这些多余的空闲的线程在被终止之前能等待新任务的时间.
* unit keepAliveTime时间的单位
* workQueue 这个是用来保留将要执行的工作队列.
* threadFactory 用于创建新线程的工厂
* handler 如果工作队列(workQueue)满了,那么这个handler是将会被执行.

ThreadPoolExecutor还有几个可不带threadFactoryhandler惨的构造器,说明java提供了一些默认的配置,让我们看一下.

如果构造不带threadFactory,那么默认使用java.util.concurrent.Executors.DefaultThreadFactory创建出一个新的工厂对象.通过阅读源代码,主要是在创建新的线程的时候修改了线程名为pool-全局线程池递增数编号-thread-当前线程池线程递增编号,让线程改为非守护线程,并设置线程的优先级为NORM_PRIORITY.

ok,再看一下handler有什么默认值.

  • java.util.concurrent.ThreadPoolExecutor.AbortPolicy 这个是默认使用的拒绝策略,如果有要执行的任务队列已满,且还有任务提交,则直接抛出异常信息
  • java.util.concurrent.ThreadPoolExecutor.DiscardPolicy 这个是忽略策略,如果有要执行的任务队列已满,且还有任务提交,则直接忽略掉这个任务,即不抛出异常也不做任何处理.
  • java.util.concurrent.ThreadPoolExecutor.DiscardOldestPolicy 忽略最早提交的任务.如果有要执行的任务队列已满,此时若还有任务提交且线程池还没有停止,则把队列中最早提交的任务抛弃掉,然后把当前任务加入队列中.
  • java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy 这个是来着不拒策略.如果有要执行的任务队列已满,此时若还有任务提交且线程池还没有停止,则直接运行任务的run方法.

例子 使用默认的拒绝策略AbortPolicy

public class Demo1 {
    public static void main(String[] args) {
        BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(10);
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
        ThreadPoolExecutor pool = new ThreadPoolExecutor(3, 3, 0, TimeUnit.SECONDS, queue, handler);
        for (int i = 0; i < 20; i ++){
            final int temp = i;
            pool.execute(() -> {
                System.out.println("客户" + temp + "来了.......");
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        pool.shutdown();
    }
}

Exception in thread “main” java.util.concurrent.RejectedExecutionException: Task com.kco.test17.demo1.Demo1$$Lambda$1/15497079@ca494b rejected from java.util.concurrent.ThreadPoolExecutor@1a4f24f[Running, pool size = 5, active threads = 5, queued tasks = 10, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:823)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1369)
at com.kco.test17.demo1.Demo1.main(Demo1.java:16)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
pool-1-thread-1客户0来了…….
pool-1-thread-2客户1来了…….
pool-1-thread-3客户2来了…….
pool-1-thread-5客户14来了…….
pool-1-thread-4客户13来了…….
pool-1-thread-2客户3来了…….
pool-1-thread-1客户4来了…….
pool-1-thread-5客户5来了…….
pool-1-thread-3客户6来了…….
pool-1-thread-4客户7来了…….
pool-1-thread-2客户9来了…….
pool-1-thread-1客户8来了…….
pool-1-thread-3客户10来了…….
pool-1-thread-5客户11来了…….
pool-1-thread-4客户12来了…….

从结果看出来,可以看出线程是重复被使用的,而且当执行的任务超过工作队列的容量时,线程确实抛出了异常.

例子2 使用忽略策略 DiscardPolicy

RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();改为 RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardPolicy();
运行结果如下:

pool-1-thread-1客户0来了…….
pool-1-thread-3客户2来了…….
pool-1-thread-4客户13来了…….
pool-1-thread-5客户14来了…….
pool-1-thread-3客户3来了…….
pool-1-thread-4客户4来了…….
pool-1-thread-1客户5来了…….
pool-1-thread-5客户6来了…….
pool-1-thread-2客户1来了…….
pool-1-thread-3客户7来了…….
pool-1-thread-4客户8来了…….
pool-1-thread-5客户9来了…….
pool-1-thread-1客户10来了…….
pool-1-thread-2客户11来了…….
pool-1-thread-4客户12来了…….

现在线程池正确退出了,而且也不抛出异常了,但是超过工作队列容量的任务全部被忽略了.

例子3 使用忽略最早任务策略 DiscardOldestPolicy

RejectedExecutionHandler改为RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardOldestPolicy();

pool-1-thread-1客户0来了…….
pool-1-thread-2客户1来了…….
pool-1-thread-3客户2来了…….
pool-1-thread-5客户14来了…….
pool-1-thread-4客户13来了…….
pool-1-thread-4客户8来了…….
pool-1-thread-1客户11来了…….
pool-1-thread-5客户10来了…….
pool-1-thread-3客户9来了…….
pool-1-thread-2客户12来了…….
pool-1-thread-1客户15来了…….
pool-1-thread-4客户16来了…….
pool-1-thread-5客户17来了…….
pool-1-thread-2客户19来了…….
pool-1-thread-3客户18来了…….

从以上结果,我们可以看出除了客户0客户2刚好是3个核心线程被执行后,客户3客户7直接被忽略掉了.

例子4 使用来着不拒策略 CallerRunsPolicy

同样讲拒绝策略改为RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();
运行程序,结果如下:

pool-1-thread-1客户0来了…….
pool-1-thread-2客户1来了…….
pool-1-thread-3客户2来了…….
pool-1-thread-4客户13来了…….
main客户15来了…….
pool-1-thread-5客户14来了…….
pool-1-thread-2客户3来了…….
pool-1-thread-1客户4来了…….
main客户18来了…….
pool-1-thread-3客户5来了…….
pool-1-thread-4客户7来了…….
pool-1-thread-5客户6来了…….
pool-1-thread-5客户8来了…….
pool-1-thread-1客户9来了…….
pool-1-thread-4客户10来了…….
pool-1-thread-3客户12来了…….
pool-1-thread-2客户11来了…….
pool-1-thread-1客户16来了…….
pool-1-thread-5客户19来了…….
pool-1-thread-3客户17来了…….

结果,我们可以发现所有的任务都被执行,而且竟然还有两个是在主线程执行的.现在明白我之前说的则直接运行任务的run方法的意思了吧,没错是直接调用run方法,而不是开启线程去执行任务.

例子5 使用自定义的拒绝策略

现在我们自己写一个拒绝策略,要求所有的任务都必须被线程池执行,而且都要在线程池中执行.

public class Demo5 {
    public static void main(String[] args) {
        BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(10);
        RejectedExecutionHandler handler = new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                if (!executor.isShutdown()){
                    try {
                        executor.getQueue().put(r);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        ThreadPoolExecutor pool = new ThreadPoolExecutor(3, 5, 0, TimeUnit.SECONDS, queue, handler);
        for (int i = 0; i < 20; i ++){
            final int temp = i;
            pool.execute(() -> {
                String name = Thread.currentThread().getName();
                System.out.println(name + "客户" + temp + "来了.......");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        pool.shutdown();
    }
}

运行结果:

pool-1-thread-1客户0来了…….
pool-1-thread-3客户2来了…….
pool-1-thread-5客户14来了…….
pool-1-thread-4客户13来了…….
pool-1-thread-2客户1来了…….
pool-1-thread-1客户3来了…….
pool-1-thread-3客户4来了…….
pool-1-thread-5客户5来了…….
pool-1-thread-2客户6来了…….
pool-1-thread-4客户7来了…….
pool-1-thread-1客户8来了…….
pool-1-thread-3客户9来了…….
pool-1-thread-5客户10来了…….
pool-1-thread-4客户11来了…….
pool-1-thread-2客户12来了…….
pool-1-thread-1客户15来了…….
pool-1-thread-3客户16来了…….
pool-1-thread-5客户17来了…….
pool-1-thread-4客户19来了…….
pool-1-thread-2客户18来了…….

ok.所有任务都被线程池执行了.而且我们自定义的拒绝策略也很简单,就是让工作队列调用put让其一直等待,直到有可用的容量存放任务.


打赏

如果觉得我的文章写的还过得去的话,有钱就捧个钱场,没钱给我捧个人场(帮我点赞或推荐一下)
微信打赏支付宝打赏

© 著作权归作者所有

共有 人打赏支持
天之妖星
粉丝 1
博文 30
码字总数 58258
作品 0
深圳
高级程序员
私信 提问
Java多线程之线程池(ThreadPoolExecutor)实现原理分析(一)

在上一篇文章Java中实现多线程的3种方法介绍和比较中,我们讲解了Java中实现多线程的3种方法。使用多线程,就必须要考虑使用线程池,今天我们来聊聊线程池的那些事。 注:源码都是基于JDK1....

小怪聊职场
2018/05/14
0
0
Java 并发编程源码解析汇总篇

java并发编程,内存模型 java并发编程,volatile内存实现和原理 Java并发编程,并发基础 Java 并发编程,线程池(ThreadPoolExecutor)源码解析 Java并发编程,Executor 框架介绍 Java并发编...

郑加威
2018/12/23
0
0
JAVA基础再回首(三十)——JAVA基础再回首完美结束,感概万千!

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/m366917/article/details/52724939 JAVA基础再回首(三十)——JAVA基础再回首完美结束,感概万千! 经过了几...

Aduroidpc
2016/10/02
0
0
Docker最全教程之使用Docker搭建Java开发环境(十七)

Docker最全教程之使用Docker搭建Java开发环境(十七) 前言 Java是一门面向对象的优秀编程语言,市场占有率极高,但是在容器化实践过程中,发现官方支持并不友好,同时与其他编程语言的基础镜...

优惠码领取
03/13
0
0
【直播预告】阿里特邀专家徐雷Java Spring Boot开发实战系列课程(第18讲):制作Java Docker镜像与推送到DockerHub和阿里云Docker仓库

主讲人:徐雷(阿里云栖特邀Java专家) 徐雷,花名:徐雷frank;资深架构师,MongoDB中文社区联席主席,吉林大学计算机学士,上海交通大学硕士。从事了 10年+开发工作,专注于分布式架构,J...

李博bluemind
03/05
0
0

没有更多内容

加载失败,请刷新页面

加载更多

sass和less的优缺点

简述 sass和less都是css的预编译处理语言,他们引入了mixins,参数,嵌套规则,运算,颜色,名字空间,作用域,JavaScript赋值等 加快了css开发效率,当然这两者都可以配合gulp和grunt等前端构...

莫西摩西
23分钟前
0
0
信号量与PV操作

在计算机操作系统中,PV操作是进程管理中的难点。 首先应弄清PV操作的含义:PV操作由P操作原语和V操作原语组成(原语是不可中断的过程),对信号量进行操作,具体定义如下: P(S):①将信号...

shzwork
36分钟前
0
0
重新认识网络通信协议

OSI网络分层 应用层 http, smtp,pop3这些都属于应用层协议 为用户的应用程序提供服务 表示层 确保一个系统的应用层发送的信息被另一个系统的应用层接收到 会话层 通过传输层建立数据传输的通...

最胖的瘦子
50分钟前
1
0
【转】分布式数据流的轻量级异步快照

本篇翻译自论文:Lightweight Asynchronous Snapshots for Distributed Dataflows,Flink的容错快照模型即来源于该论文。原文地址:https://arxiv.org/pdf/1506.08603.pdf 分布式数据流的轻量...

yiduwangkai
53分钟前
1
0
java使用反射机制设置私有成员变量的值

写一个方法:public void setProperty(Objectobj, String propertyName, Object value){}, 此方法可将obj对象中名为propertyName的属性的值设置为value。(这里不知道obj对象的propertyNam...

群星纪元
59分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部