thread_ThreadPoolExecutor
目录
1.基础知识
2.简单应用
3.异常机制
4.丰富的扩展
一.基础知识
构造函数。
public ThreadPoolExecutor(
int corePoolSize, 指的是保留的线程池大小
int maximumPoolSize, 指的是线程池的最大大小
long keepAliveTime, 指的是空闲线程结束的超时时间
TimeUnit unit,
BlockingQueue<Runnable> workQueue) 表示存放任务的队列
工作过程:
1 、线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。
2 、当调用 execute() 方法添加一个任务时,线程池会做如下判断:
a. 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;
b. 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列。
c. 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创建线程运行这个任务;
d. 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会抛出异常,告诉调用者“我不能再接受任务了”。
3 、当一个线程完成任务时,它会从队列中取下一个任务来执行。
4 、当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。
二.简单应用
public void threadPool1Test() throws InterruptedException {
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
ThreadPoolExecutor exe = new ThreadPoolExecutor(2, 6, 1, TimeUnit.DAYS, queue);
try {
int threadNum = 0;
for (int i = 0; i < 10; i++) {
threadNum++;
final int currentThreadNum = threadNum;
exe.execute(new Runnable() {
@Override
public void run() {
try {
System.out.println("子线程[" + currentThreadNum + "]开启");
Thread.sleep(1000 * 10);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("子线程[" + currentThreadNum + "]结束");
}
}
});
} //不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务
System.out.println("已经开启所有的子线程");
exe.shutdown();
System.out.println("shutdown():启动一次顺序关闭,执行以前提交的任务,但不接受新任务。"); //判断线程池所有任务是否执行完毕
while (true) {
if (exe.isTerminated()) {
System.out.println("所有的子线程都结束了!");
break;
}
Thread.sleep(1000);
System.out.println("线程池中线程数目:"+exe.getPoolSize()+
",队列中等待执行的任务数目:"+ exe.getQueue().size()+
",已执行玩别的任务数目:"+exe.getCompletedTaskCount());
System.out.println("线程队列大小为-->"+queue.size());
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("主线程结束");
}
}
1、BlockingQueue 只是一个接口,常用的实现类有 LinkedBlockingQueue 和 ArrayBlockingQueue。用 LinkedBlockingQueue 的好处在于没有大小限制。这样的话,因为队列不会满,所以 execute() 不会抛出异常,而线程池中运行的线程数也永远不会超过 corePoolSize 个,keepAliveTime 参数也就没有意义了。
2、shutdown() 方法不会阻塞。调用 shutdown() 方法之后,主线程就马上结束了,而线程池会继续运行直到所有任务执行完才会停止。如果不调用 shutdown() 方法,那么线程池会一直保持下去,以便随时添加新的任务。
二.异常处理
线程超出了线程池的总容量(线程队列大小+最大线程数)
@Test
public void threadPool2Test() throws InterruptedException {
ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 6, 1, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<Runnable>(4));
//executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.setRejectedExecutionHandler(
new RejectedExecutionHandler() {
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println(String.format("Task %d rejected.", r.hashCode()));
//System.out.println("DemoTask Rejected : " + ((DemoThread) r).getName());
System.out.println("Waiting for a second !!");
try {
Thread.sleep(1000);
executor.execute(r);
//executor.getQueue().put(r);
} catch (InterruptedException e) {
}
}
});
///////
for (int i = 0; i < 11; i++) {
final int taskNum = i;
Runnable myTask = new Runnable() {
@Override
public void run() {
System.out.println("正在执行task " + taskNum); try {
Thread.currentThread().sleep(100 * (new Random()).nextInt(8));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("task " + taskNum + "执行完毕");
}
};
executor.execute(myTask);
}
// 不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务
executor.shutdown();
while (true) {
if (executor.isTerminated()) {
System.out.println("所有的子线程都结束了!");
break;
}
Thread.sleep(1000);
System.out.println("线程池中线程数目:" + executor.getPoolSize() + ",队列中等待执行的任务数目:" + executor.getQueue().size()
+ ",已执行玩别的任务数目:" + executor.getCompletedTaskCount());
}
}
ThreadPoolExecutor 提供 4 个现有的策略,分别是:
ThreadPoolExecutor.AbortPolicy:表示拒绝任务并抛出异常
ThreadPoolExecutor.DiscardPolicy:表示拒绝任务但不做任何动作
ThreadPoolExecutor.CallerRunsPolicy:表示拒绝任务,并在调用者的线程中直接执行该任务
ThreadPoolExecutor.DiscardOldestPolicy:表示先丢弃任务队列中的第一个任务,然后把这个任务加进队列。
使用CallerRunsPolicy,会将所有线程都执行到,也不可避免的会使用主线程来加载一个线程任务。一次同时创建maximumPoolSize个线程,加上主线程,就是一次执行maximumPoolSize+1个线程任务,等执行完后才会,再执行maximunPoolSize+1个线程任务,当然创建的maximumPoolSize个线程也会复用。
显然这种策略保证了每一个线程任务都会执行,但牺牲了性能。而,DiscardPolicy和DiscardOldestPolicy都会抛弃容纳不了的线程任务,保证了性能。
a. ThreadPoolExecutor可以设置一个“拒绝策略”,这是指当一个task被拒绝添加到线程池中时,采取的处理措施,
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
b.task非常重要,“等得起”,但是“丢不起”
System.out.println("Waiting for a second !!");
try {
Thread.sleep(1000);
executor.execute(r);
} catch (InterruptedException e) {
}
c.利用RejectedExecutionHandler来阻塞submit()?
首先 submit()方法是调用了workQueue的offer()方法来塞入task,而offer()方法是非阻塞的,当workQueue已经满的时候,offer()方法会立即返回false,并不会阻塞在那里等待workQueue有空出位置,所以要让submit()阻塞,关键在于改变向workQueue添加task的行为,
if (!executor.isShutdown()) {
try {
executor.getQueue().put(r);
} catch (InterruptedException e) {
}
}
调用了getQueue()方法,得到了workQueue,再调用其put()方法,将task放到workQueue中,而这个put()方法是阻塞的
当workQueue满时,submit()一个task会导致调用我们自定义的RejectedExecutionHandler,而我们自定义的RejectedExecutionHandler会保证该task继续被尝试 用阻塞式的put()到workQueue中。
d.重写了offer()方法的BlockingQueue
由于submit()是调用workQueue的offer()方法来添加task的,而offer()是非阻塞的,所以,如果我们自己实现一个BlockingQueue,其offer()方法是阻塞的
public class LimitedQueue<E> extends LinkedBlockingQueue<E> {
public LimitedQueue(int maxSize) {
super(maxSize);
}
@Override
public boolean offer(E e) {
// turn offer() and add() into a blocking calls (unless interrupted)
try {
put(e);
return true;
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
return false;
}
}
四.丰富的可扩展性
1.扩展生成器,如线程的创建前beforeExecute,创建后afterExecute,退出terminated;
线程的创建策略, 通过重构ThreadFactory 重构线程名,设置守护线程等;
线程的拒绝策略,日志,拒绝,重试等
线程运行抛出异常机制
class ExtThreadPoolExecutor extends ThreadPoolExecutor { public ExtThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
super.setThreadFactory(new ExtThreadFactory());
super.setRejectedExecutionHandler(new ExtRejectedExecutionHandler());
} @Override
protected void beforeExecute(Thread t, Runnable r) {
super.beforeExecute(t, r);
System.out.println("Perform beforeExecute() ");
}
@Override
protected void terminated() { System.out.println("线程池退出 ");
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (t != null) {
System.out.println("Perform exception handler logic");
}
System.out.println("Perform afterExecute() ");
}
/////////////////////////////////
// @Override
// public void execute(Runnable task) {
// super.execute(wrap(task,clientTrace(),Thread.currentThread().getName()));
// }
// @Override
// public Future<?> submit(Runnable task) {
// return super.submit(wrap(task,clientTrace(),Thread.currentThread().getName()));
// }
private Exception clientTrace(){
return new Exception("client stack trace");
}
private Runnable wrap(final Runnable task ,final Exception clientStack,String clientThreadName ){
return new Runnable(){
@Override
public void run() {
try {
task.run();
} catch (Exception e) {
clientStack.printStackTrace();
throw e;
}
task.run(); }
}; }
///////////////////////////////////////////
private class ExtThreadFactory implements ThreadFactory {
private AtomicInteger count = new AtomicInteger(0);
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
String threadName = ExtThreadPoolExecutor.class.getSimpleName() + count.addAndGet(1);
System.out.println("create "+threadName);
//t.setDaemon(true);
t.setName(threadName);
return t;
}
} //
private class ExtRejectedExecutionHandler implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println("DemoTask Rejected : " + ((DemoThread) r).getName());
System.out.println("Waiting for a second !!"); if (!executor.isShutdown()) {
try {
Thread.sleep(1000);
// executor.execute(r);
executor.getQueue().put(r);
// 当task被拒绝添加到线程池中时,ThreadPoolExecutor会采用“丢弃”策略来对待这个任务,即这个task被丢弃了。
// System.out.println("Lets add another time : " + ((DemoThread) r).getName());
} catch (InterruptedException e) {
}
}
}
}
}
2.扩展生成器
class DemoThread implements Runnable {
private String name = null; public DemoThread(String name) {
this.name = name;
} public String getName() {
return this.name;
} @Override
public void run() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Executing : taskname: " + name +", Thread id: " +Thread.currentThread().getId());
}
}
public static void main(String[] args) { BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<Runnable>(50); ExtThreadPoolExecutor pool = new ExtThreadPoolExecutor(10, 20, 5000, TimeUnit.MILLISECONDS,
blockingQueue); for (int i = 1; i < 100; i++) {
System.out.println("提交第" + i + "个任务!");
pool.execute(new DemoThread(Integer.valueOf(i).toString() ) );
pool.submit(new DemoThread(Integer.valueOf(i).toString() ));
} // 2.销毁----此处不能销毁,因为任务没有提交执行完,如果销毁线程池,任务也就无法执行了
// exec.destory(); try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
} }
thread_ThreadPoolExecutor的更多相关文章
随机推荐
- LogViewer - 方便的日志查看工具
一个完整的程序日志记录功能是必不可少的,通过日志我们可以了解程序运行详情.错误信息等,以便更好的发现及解决问题. 日志可以记录到数据库.日志服务器.文件等地方,本文主要介绍文件日志. 文件日志通常是一 ...
- 将json转换成struts参数
加入对象为{name:'tom','class':{className:'class1'},classMates:[{name:'lily'}]}struts2期待的格式是 name=tom& ...
- 通过Web.config实现301重定向
通过Web.config实现301重定向 IIS7以上可以通过修改Web.config实现IIS设置 现在我们通过Web.config实现301重定向 <system.webServer> ...
- 最近使用ajaxFileUpload和Jcrop来实现图片上传和截图,出现一个图片无法更换的问题
使用setImage 都无法更换 刷新图片 找了很久 什么方法都找过,最后发现...... 原来是 上传的图片的命名问题... 每次上传的图片 保存后都是同样的图片, 所以返回路径都是一样... jc ...
- 纯CSS实现图片抖动
实现方法: 1.将上文提到的以下JS内容删除: $(".imagelogo").mouseover(function() { obj = $(this); i = 5; timer ...
- Linux之date
近期学习需要根据不同的日期的数据,做同样的操作,如果一遍遍的手动操作,太笨重了,有些愚公的味道.所以就想着在shell下,将时间当做变量,然后重复其他的操作. 这里的需求是得到从某天(例如:2014- ...
- 为什么 NSLog 不支持 Swift 对象(转)
https://segmentfault.com/a/1190000005668218 也就说: 1. 如果是 OC 的对象,重写 description 的 get 方法就可以了. 2. 如果不是 ...
- JVM内存溢出及合理配置
Tomcat本身不能直接在计算机上运行,需要依赖于硬件基础之上的操作系统和一个Java虚拟机.Tomcat的内存溢出本质就是JVM内存溢出,所以在本文开始时,应该先对Java JVM有关内存方面的知识 ...
- Spark源码系列(三)作业运行过程
作业执行 上一章讲了RDD的转换,但是没讲作业的运行,它和Driver Program的关系是啥,和RDD的关系是啥? 官方给的例子里面,一执行collect方法就能出结果,那我们就从collect开 ...
- Android之TextView文字绘制流程
一:TextView的onDraw()方法: 1.第一句restartMarqueeIfNeeded()绘制字幕滚动. protected void onDraw(Canvas canvas) { r ...