并发编程 13—— 线程池的使用 之 配置ThreadPoolExecutor 和 饱和策略
Java并发编程实践 目录
并发编程 04—— 闭锁CountDownLatch 与 栅栏CyclicBarrier
并发编程 06—— CompletionService : Executor 和 BlockingQueue
并发编程 10—— 任务取消 之 关闭 ExecutorService
并发编程 12—— 任务取消与关闭 之 shutdownNow 的局限性
并发编程 13—— 线程池的使用 之 配置ThreadPoolExecutor 和 饱和策略
第1 部分 配置ThreadPoolExecutor
1.1 创建
ThreadPoolExecutor为一些Executor提供了基本的实现,比如,newCachedThreadPool,newFixedThreadPool等等。ThreadPoolExecutor允许各种定制
它的构造函数如下:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {}
最常用的newCachedThreadPool。
1.2 管理队列任务
据线程池的大小来选择合适的队列有利于充分利用资源和防止耗尽资源。
基本的排队方法有3种:
- 无界队列
- 有界队列
- 同步移交
1.3 饱和策略
我们在ThreadPoolExecutor的构造函数中看到了最后一个参数。 RejectedExecutionHandler handler。这个就是饱和策略。
JDK提供了几种不同的实现:
- DiscardOldestPolicy
- AbortPolicy
- CallerRunsPolicy
- discardPolicy
AbortPolicy是默认的饱和策略,就是中止任务,该策略将抛出RejectedExecutionException。调用者可以捕获这个异常然后去编写代码处理异常。
当新提交的任务无法保存到队列中等待执行时,DiscardPolicy会稍稍的抛弃该任务,DiscardOldestPolicy则会抛弃最旧的(下一个将被执行的任务),然后尝试重新提交新的任务。如果工作队列是那个优先级队列时,搭配DiscardOldestPolicy饱和策略会导致优先级最高的那个任务被抛弃,所以两者不要组合使用。
CallerRunsPolicy是“调用者运行”策略,实现了一种调节机制 。它不会抛弃任务,也不会抛出异常。 而是将任务回退到调用者。它不会在线程池中执行任务,而是在一个调用了Executor的线程中执行该任务。
/**
* 调用者运行的饱和策略
* @ClassName: ThreadDeadlock2
* TODO
* @author xingle
* @date 2014-11-20 下午4:18:11
*/
public class ThreadDeadlock2 {
ExecutorService exec = new ThreadPoolExecutor(0, 2, 60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),new ThreadPoolExecutor.CallerRunsPolicy()); private void putrunnable() {
for (int i = 0; i < 4; i++) {
exec.submit(new Runnable() { @Override
public void run() {
// TODO Auto-generated method stub
while (true) {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
}
}
public static void main(String[] args) {
new ThreadDeadlock2().putrunnable();
}
}
执行结果:

当工作队列被填满之后,没有预定义的饱和策略来阻塞execute。通过使用 Semaphore (信号量)来限制任务的到达率,就可以实现这个功能。在下面的BoundedExecutor 中给出了这种方法,该方法使用了一个无界队列,并设置信号量的上界设置为线程池的大小加上可排队任务的数量,这是因为信号量需要控制正在执行的和正在等待执行的任务数量。
/**
* 8.4 使用Semaphore来控制任务的提交速率
* @ClassName: BoundedExecutor
* TODO
* @author xingle
* @date 2014-11-20 下午2:46:19
*/
public class BoundedExecutor {
private final Executor exec;
private final Semaphore semaphore;
int bound; public BoundedExecutor(Executor exec,int bound){
this.exec = exec;
this.semaphore = new Semaphore(bound);
this.bound = bound;
} public void submitTask(final Runnable command) throws InterruptedException{
//通过 acquire() 获取一个许可
semaphore.acquire();
System.out.println("----------当前可用的信号量个数:"+semaphore.availablePermits());
try {
exec.execute(new Runnable() { @Override
public void run() {
try {
System.out.println("线程" + Thread.currentThread().getName() +"进入,当前已有" + (bound-semaphore.availablePermits()) + "个并发");
command.run();
} finally {
//release() 释放一个许可
semaphore.release();
System.out.println("线程" + Thread.currentThread().getName() +
"已离开,当前已有" + (bound-semaphore.availablePermits()) + "个并发");
}
}
});
} catch (RejectedExecutionException e) {
semaphore.release();
}
}
}
测试程序:
public class BoundedExecutor_main {
public static void main(String[] args) throws InterruptedException{
ExecutorService exec = Executors.newCachedThreadPool();
BoundedExecutor e = new BoundedExecutor(exec, 3);
for(int i = 0;i<5;i++){
final int c = i;
e.submitTask(new Runnable() {
@Override
public void run() {
System.out.println("执行任务:" +c);
}
});
}
}
}
执行结果:

参考:
并发编程 13—— 线程池的使用 之 配置ThreadPoolExecutor 和 饱和策略的更多相关文章
- JUC 并发编程--08,线程池,三大方法,七大参数,4种拒绝策略,代码演示
三大方法: //线程池核心线程数为n, 最大线程数为 n ExecutorService fixedThreadPool = Executors.newFixedThreadPool(n); 源码: ...
- 13、Java并发编程:线程池的使用
Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...
- Java并发编程:线程池的使用
Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...
- Java并发编程:线程池的使用(转)
Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...
- Java并发编程:线程池的使用(转载)
转载自:https://www.cnblogs.com/dolphin0520/p/3932921.html Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实 ...
- Java并发编程:线程池的使用(转载)
文章出处:http://www.cnblogs.com/dolphin0520/p/3932921.html Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实 ...
- [转]Java并发编程:线程池的使用
Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...
- 【转】Java并发编程:线程池的使用
Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...
- (转)Java并发编程:线程池的使用
背景:线程池在面试时候经常遇到,反复出现的问题就是理解不深入,不能做到游刃有余.所以这篇博客是要深入总结线程池的使用. ThreadPoolExecutor的继承关系 线程池的原理 1.线程池状态(4 ...
随机推荐
- box-flex不均分问题
解决box-flex不均等分的问题 我想当你上手css3的时候后一定为他的强大而感到震惊,但是震惊之后带来的一定是苦恼,因为他太TM变态了! 我之所以这么说是因为我今天写box-flex的时候遇到了一 ...
- div显示滚动条
div显示上下左右滚动条 <div style="width:260px;height:120px; overflow:scroll; border:1px solid;"& ...
- paper 99:CV界的明星人物经典介绍
CV人物1:Jianbo Shi史建波毕业于UC Berkeley,导师是Jitendra Malik.其最有影响力的研究成果:图像分割.其于2000年在PAMI上多人合作发表”Nor ...
- LINQ to SQL Count/Sum/Min/Max/Avg Join
public class Linq { MXSICEDataContext Db = new MXSICEDataContext(); // LINQ to SQL // Count/Sum/Min/ ...
- SSH 无密码远程执行脚本
ssh无密码登录及远程执行脚本要使用公钥与私钥.linux下可以用用ssh-keygen生成公钥/私钥对,下面我以CentOS7为例. 测试环境:机器A(10.0.224.80):机器B(192.16 ...
- CentOS7安装memcached
三台linux服务器系统CentOS7 一台memcached IP:192.168.155.134 一台Apache IP:192.168.155.130 一台nginx IP:192.168.15 ...
- 实用Redis操作类
<?php /** * ------------------------------------------ * 统一redis的配置与数据存储规范,便于扩展与修改 * # redis通常用于热 ...
- 【转】MYSQL启用日志,和查看日志
转载地址:http://wasabi.iteye.com/blog/318962 mysql有以下几种日志: 错误日志: -log-err 查询日志: -log ...
- [转]使用xcode4 workspace 多个project协同工作
转载地址:http://erlangprogram.com/use-xcode4-workspace 一般的某个应用单独新建一个 project 就可以了,然后把所有的程序文件都放在里面,这个可以 ...
- 【转】稍改进过的ListView,很好用哈
using System; using System.Collections.Generic; using System.Text; using System.Windows.Forms; using ...