并发编程 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 ...
随机推荐
- MySQL主键删除/添加
2修改数据库和表的字符集alter database maildb default character set utf8;//修改数据库的字符集alter table mailtable defaul ...
- Oracle 小案例
create database cstd; use cstd; /*1:建立学生表*/ create table student ( 学号 ) primary key, 姓名 ), 性别 ), 年龄 ...
- JAVA多线程实现的三种方式
JAVA多线程实现方式主要有三种:继承Thread类.实现Runnable接口.使用ExecutorService.Callable.Future实现有返回结果的多线程.其中前两种方式线程执行完后都没 ...
- 12个css高级技巧.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- when will a databasechange be committed?
1) Database-updates via DML in a SQLExec-statement (e.g. INSERT INTO PS_TEST_TABLE VALUES(‘value_fie ...
- css布局之左侧固定右侧自适应布局
参考代码如下: <form id="form1" style="height:100%; overflow:hidden;"> <div st ...
- IsBackground的理解
1.当在主线程中创建了一个线程,那么该线程的IsBackground默认是设置为FALSE的. 2.当主线程退出的时候,IsBackground=FALSE的线程还会继续执行下去,直到线程执行结束. ...
- [转]关于NSAutoreleasePool' is unavailable: not available in automatic reference counting mode的解决方法
转载地址:http://blog.csdn.net/xbl1986/article/details/7216668 Xcode是Version 4.2 Build 4D151a 根据Objective ...
- NodeJS的安装
1. 进入官网下载对应版本的nodejs(我选择的是32位的window7版本x86) 2. 默认路径可修改 3. install就可以安装在cmd中输入path查看路径(我的是E:\compan ...
- 关于JQ的$.deferred()
此文章是在网络上看到的. 一.什么是deferred对象? 开发网站的过程中,我们经常遇到某些耗时很长的javascript操作.其中,既有异步的操作(比如ajax读取服务器数据),也有同步的操作(比 ...