目录

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的更多相关文章

随机推荐

  1. Apple开发者网站中一些比较有用的文档

    支持IPv6 DNS64/NAT64网络 关于plist文件中的键与值 Apple各种审核准则以及设计准则

  2. HTTP - GET和POST的区别

    网上有很多文章介绍这两种HTTP请求的区别,我也不懂,主要还是看了一些文章,在这里写下一些笔记. 语义不同 在HTTP协议中,最初规定GET是用来查询或者获取资料,只读,POST用于修改数据,可写.因 ...

  3. js中“==”与"==="的区别

    首先,== equality 等同,=== identity 恒等. ==, 两边值类型不同的时候,要先进行类型转换,再比较. ===,不做类型转换,类型不同的一定不等. 一言以蔽之:==先转换类型再 ...

  4. 持续集成(二)环境搭建篇—内网邮件server搭建

    在我们的持续构建中,项目构建中出现错误提醒.或者开发者之间的沟通交流,进度汇报的事务,都是离不开一个通信工具.那就是邮件.在我们的项目开发中假设使用第三方的邮件平台,这肯定不是最好的选择.由于第三方的 ...

  5. fio terse输出详解

    fio, the flexible IO tester, is a very useful tool for benchmarking IO performance. It has an option ...

  6. 深入剖析 redis AOF 持久化策略

    本篇主要讲的是 AOF 持久化,了解 AOF 的数据组织方式和运作机制.redis 主要在 aof.c 中实现 AOF 的操作. 数据结构 rio redis AOF 持久化同样借助了 struct ...

  7. android OOM分析工具LeakCanary

    http://my.oschina.net/u/255456/blog/523659?fromerr=oGosxKBf LeakCanary 只是探测到可能出现内存泄露,然后dump 一个java h ...

  8. sql 查询 – left join on

      1. 问题引入   主要是为了查询在一个表中出现,而不在另一个表中出现的数据,具体来说: 如下图所示, 有A.B两个表,其中B表的Aid字段参照A表的主键id,为了查询在A表中出现,却没有被B表引 ...

  9. 如果你遇到,在IntelliJ IDEA里Ctrl+Alt+方向键用不了

    在idea中使用ctrl+b跟踪进入函数之后,每次返回都不知道用什么快捷键,在idea中使用ctrl+alt+方向键首先会出现与win7屏幕方向的快捷键冲突,右键桌面,选择图形属性,将win7的快捷键 ...

  10. jqGrid标题行与第一行之间有很大空白的问题解决。

    如题的问题,网上找了很久,都没有解决方案.最后发现,问题不在jqgrid的配置代码,问题在前台HTML代码. <table id="grid" height="30 ...