java多线程之ThreadPoolExecutor类

多线程运算是一种有效提升设备吞吐量和运行效率的方法,java环境也为开发者提供了封装好的多线程运算方式,即线程池。java工厂类Executors为开发者提供了四种直接创建线程池的方法,而这四种线程池创建方法都是通过继承和复写ThreadPoolExecutor构造类而成,因此利用ThreadPoolExecutor类亦可创建线程池。

1、四种Executors下的创建线程池方法

Executors主要提供了下面几种创建线程池的方法:

(1)newFixedThreadPool方法

创建一个固定大小的线程池,每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程线

  1. public class FixedThreadPool {  
  2.     public static void main(String[] args) {  
  3.         ExecutorService pool = Executors.newFixedThreadPool(5);// 创建一个固定大小为5的线程池  
  4.         for (int i = 0; i < 10; i++) {  
  5.             pool.submit(new MyThread());  
  6.         }  
  7.         pool.shutdown();  
  8.     }  
  9. }  


(2)newSingleThreadPoolfangfa

创建一个单线程的线程池,这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

  1. public class SingleThreadPool {  
  2.     public static void main(String[] args) {    
  3.         ExecutorService pool=Executors.newSingleThreadExecutor();//创建一个单线程池    
  4.         for(int i=0;i<100;i++){    
  5.             pool.submit(new MyThread());    
  6.         }    
  7.         pool.shutdown();    
  8.     }  
  9. }  


(3) newScheduledThreadPool
public class ScheduledThreadPool {  
    public static void main(String[] args) {    
        ScheduledExecutorService pool=Executors.newScheduledThreadPool(6);    //创建线程池大小为6
        for(int i=0;i<10000;i++){    
            pool.submit(new MyThread());    
        }    
            
        pool.schedule(new MyThread(), 1000, TimeUnit.MILLISECONDS);    //设定时间周期为1000ms
        pool.schedule(new MyThread(), 1000, TimeUnit.MILLISECONDS);    
        pool.shutdown();    
    }    
} 
创建一个线程执行之间有固定时间周期的线程池,当上一个线程任务在固定时间内执行完毕时,环境会延时直到达到固定时间再执行下一个线程,若执行时间超出固定时间也不会中断

(4)newCachedThreadPool

  1. public class CachedThreadPool {  
  2.     public static void main(String[] args) {    
  3.         ExecutorService pool=Executors.newCachedThreadPool();    
  4.         for(int i=0;i<100;i++){    
  5.             pool.submit(new MyThread());    
  6.         }    
  7.         pool.shutdown();    
  8.     }    
  9. }  
创建一个带有缓存的线程池。 如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程从而减小线程池规模,当任务数增加时,此线程池又可以添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

官方建议程序员使用较为方便的Executors工厂方法Executors.newCachedThreadPool()(无界线程池,可以进行自动线程回收)、Executors.newFixedThreadPool(int)(固定大小线程池)Executors.newSingleThreadExecutor()(单个后台线程),这几种线程池均为大多数使用场景预定义了默认配置。

2、ThreadPoolExecutor类

java.uitl.concurrent.ThreadPoolExecutor类是线程池中最核心的类,也是上述四种构造线程池方法的核心调用类。因此如果要深入理解Java中的线程池,必须深入理解这个类。

Executor是一个接口,跟线程池有关的基本都要跟他打交道。下面是常用的ThreadPoolExecutor的关系。

这里写图片描述

Executor接口很简单,只有一个execute方法。

ExecutorService是Executor的子接口,增加了一些常用的对线程的控制方法,之后使用线程池主要也是使用这些方法。AbstractExecutorService是一个抽象类。ThreadPoolExecutor就是实现了这个类

ThreadPoolExecutor类提供了如下四种构造方法
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);
    }

   
 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), handler);
    }

 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }
首先,ThreadPoolExecutor继承了AbstractExecutorService类的抽象结构并具体实现,并提供了四个构造器,事实上,ThreadPoolExecutor前面三个构造器都是调用的第四个构造器进行的初始化工作。
ThreadPoolExecutor构造器中的各种参数解释如下:

corePoolSize--池中所保存的线程数,包括空闲线程。

maximumPoolSize--池中允许的最大线程数。

keepAliveTime--当线程数大于corePoolSize时,此为终止空闲线程等待新任务的最长时间。

Unit--keepAliveTime 参数的时间单位。

workQueue--执行前用于保持任务的队列。此队列仅保持由 execute方法提交的 Runnable任务。

threadFactory--执行程序创建新线程时使用的工厂。

Handler--由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。

重点重点重点-几个参数之间的关系

当线程池刚创建的时候,线程池里面是没有任何线程的(注意,并不是线程池一创建,里面就创建了一定数量的线程,当调用execute()方法添加一个任务时,线程池会做如下的判断和操作:

1)如果当前正在运行的线程数量小于corePoolSize,那么立刻创建一个新的线程,执行这个任务。

2)如果当前正在运行的线程数量大于或等于corePoolSize,那么这个任务将会放入队列中。

3)如果线程池的队列已经满了,但是正在运行的线程数量小于maximumPoolSize,那么还是会创建新的线程,执行这个任务。

4)如果队列已经满了,且当前正在运行的线程数量大于或等于maximumPoolSize,那么线程池会根据拒绝执行策略来处理当前的任务。

5)当一个任务执行完后,线程会从队列中取下一个任务来执行,如果队列中没有需要执行的任务,那么这个线程就会处于空闲状态,如果超过了keepAliveTime存活时间,则这个线程会被线程池回收(注:回收线程是有条件的,如果当前运行的线程数量大于corePoolSize的话,这个线程就会被销毁,如果不大于corePoolSize,是不会销毁这个线程的,运行的线程数量必须保持在corePoolSize数量内).为什么不是线程一空闲就回收,而是需要等到超过keepAliveTime才进行线程的回收了,原因很简单:因为线程的创建和销毁消耗很大,更不能频繁的进行创建和销毁,当超过keepAliveTime后,发现确实用不到这个线程了,才会进行销毁。其中unit表示keepAliveTime的时间单位,unit的结构定义类Timeunit如下:

public enum TimeUnit {  
    NANOSECONDS {  
        // keepAliveTime以纳秒为单位  
    },  
    MICROSECONDS {  
       // keepAliveTime以微秒为单位  
    },  
    MILLISECONDS {  
       // keepAliveTime以毫秒为单位  
    },  
    SECONDS {  
        // keepAliveTime以秒为单位  
    },  
    MINUTES {  
       // keepAliveTime以分钟为单位  
    },  
    HOURS {  
       // keepAliveTime以小时为单位  
    },  
    DAYS {  
        // keepAliveTime以天为单位  
    };  

对于上面的几种情况,主要涉及到的源码有以下几块:

[java]  view plain  copy
  1. private boolean addIfUnderCorePoolSize(Runnable firstTask) {  
  2.         Thread t = null;  
  3.         final ReentrantLock mainLock = this.mainLock;  
  4.         mainLock.lock();  
  5.         try {  
  6.             if (poolSize < corePoolSize && runState == RUNNING)  
  7.                 t = addThread(firstTask);  
  8.         } finally {  
  9.             mainLock.unlock();  
  10.         }  
  11.         if (t == null)  
  12.             return false;  
  13.         t.start();  
  14.         return true;  
  15. }  
  16.   
  17. 其实,这段代码很简单,主要描述的就是,如果当前的线程池小于corePoolSize的时候,是直接新建一个线程来处理任务。  
  18. private boolean addIfUnderMaximumPoolSize(Runnable firstTask) {  
  19.         Thread t = null;  
  20.         final ReentrantLock mainLock = this.mainLock;  
  21.         mainLock.lock();  
  22.         try {  
  23.             if (poolSize < maximumPoolSize && runState == RUNNING)  
  24.                 t = addThread(firstTask);  
  25.         } finally {  
  26.             mainLock.unlock();  
  27.         }  
  28.         if (t == null)  
  29.             return false;  
  30.         t.start();  
  31.         return true;  
  32. }  
  33.   
  34. 上面这段代码描述的是,如果当前线程池的数量小于maximumPoolSize的时候,也会创建一个线程,来执行任务
又一个重点重点-线程池的队列

workQueue是关于线程池阻塞队列策略的选择参数

线程池的队列,总的来说有3种:

直接提交:工作队列的默认选项是 SynchronousQueue,它将任务直接提交给线程而不保持它们。在此,如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此会构造一个新的线程。此策略可以避免在处理可能具有内部依赖性的请求集时出现锁。直接提交通常要求无界 maximumPoolSizes 以避免拒绝新提交的任务。当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。

无界队列:使用无界队列(例如,不具有预定义容量的 LinkedBlockingQueue)将导致在所有 corePoolSize 线程都忙时新任务在队列中等待。这样,创建的线程就不会超过 corePoolSize。(因此,maximumPoolSize的值也就无效了。)当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;例如,在 Web页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。

有界队列:当使用有限的 maximumPoolSizes时,有界队列(如 ArrayBlockingQueue)有助于防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大限度地降低 CPU 使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。如果任务频繁阻塞(例如,如果它们是 I/O边界),则系统可能为超过您许可的更多线程安排时间。使用小型队列通常要求较大的池大小,CPU使用率较高,但是可能遇到不可接受的调度开销,这样也会降低吞吐量。

下面就来说下线程池的队列,类结构图如下:


1)SynchronousQueue

该队列对应的就是上面所说的直接提交,首先SynchronousQueue是无界的也就是说他存数任务的能力是没有限制的,但是由于该Queue本身的特性,在某次添加元素后必须等待其他线程取走后才能继续添加。

2)LinkedBlockingQueue

该队列对应的就是上面的无界队列。如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;

3)ArrayBlockingQueue

该队列对应的就是上面的有界队列,此队列创建时必须指定大小ArrayBlockingQueue有以下3中构造方法:

[java]  view plain  copy
  1. public ArrayBlockingQueue(int capacity) {  
  2.         this(capacity, false);  
  3.     }  
  4.   
  5.       
  6.     public ArrayBlockingQueue(int capacity, boolean fair) {  
  7.         if (capacity <= 0)  
  8.             throw new IllegalArgumentException();  
  9.         this.items = (E[]) new Object[capacity];  
  10.         lock = new ReentrantLock(fair);  
  11.         notEmpty = lock.newCondition();  
  12.         notFull =  lock.newCondition();  
  13. }  
  14.   
  15.     public ArrayBlockingQueue(int capacity, boolean fair,  
  16.                               Collection<? extends E> c) {  
  17.         this(capacity, fair);  
  18.         if (capacity < c.size())  
  19.             throw new IllegalArgumentException();  
  20.   
  21.         for (Iterator<? extends E> it = c.iterator(); it.hasNext();)  
  22.             add(it.next());  
  23.     }  

重点来说下这个fairfair表示队列访问线程的竞争策略,当为true的时候,任务插入队列遵从FIFO的规则,如果为false,则可以“插队”。举个例子,假如现在有很多任务在排队,这个时候正好一个线程执行完了任务,同时又新来了一个任务,如果为false的话,这个任务不用在队列中排队,可以直接插队,然后执行。如下图所示:


threadFactory:线程工厂,主要用来创建线程;
handler:表示当拒绝处理任务时的策略,有以下四种取值:
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。 
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务 

再来一个重点重点,ThreadPoolExecutor类中的几个常用方法

在ThreadPoolExecutor类中有几个非常重要的方法:

execute()
submit()
shutdown()
shutdownNow()

execute()方法实际上是Executor中声明的方法,在ThreadPoolExecutor进行了具体的实现,这个方法是ThreadPoolExecutor的核心方法,

通过这个方法可以向线程池提交一个任务,交由线程池去执行。

submit()方法是在ExecutorService中声明的方法,在AbstractExecutorService就已经有了具体的实现,

在ThreadPoolExecutor中并没有对其进行重写,这个方法也是用来向线程池提交任务的,但是它和execute()方法不同,

它能够返回任务执行的结果,去看submit()方法的实现,会发现它实际上还是调用的execute()方法,

只不过它利用了Future来获取任务执行结果(Future相关内容将在下一篇讲述)。

shutdown()和shutdownNow()是用来关闭线程池的。

Executor几个相关类的具体实现

从上面给出的ThreadPoolExecutor类的代码可以知道,ThreadPoolExecutor继承了AbstractExecutorService,看一下AbstractExecutorService的实现:

复制代码
public abstract class AbstractExecutorService implements ExecutorService {
 
     
    protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) { };
    protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) { };
    public Future<?> submit(Runnable task) {};
    public <T> Future<T> submit(Runnable task, T result) { };
    public <T> Future<T> submit(Callable<T> task) { };
    private <T> T doInvokeAny(Collection<? extends Callable<T>> tasks,
                            boolean timed, long nanos)
        throws InterruptedException, ExecutionException, TimeoutException {
    };
    public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
        throws InterruptedException, ExecutionException {
    };
    public <T> T invokeAny(Collection<? extends Callable<T>> tasks,
                           long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException {
    };
    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
        throws InterruptedException {
    };
    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
                                         long timeout, TimeUnit unit)
        throws InterruptedException {
    };
}
复制代码

AbstractExecutorService是一个抽象类,它实现了ExecutorService接口。

我们看ExecutorService接口的实现:

复制代码
public interface ExecutorService extends Executor {
 
    void shutdown();
    boolean isShutdown();
    boolean isTerminated();
    boolean awaitTermination(long timeout, TimeUnit unit)
        throws InterruptedException;
    <T> Future<T> submit(Callable<T> task);
    <T> Future<T> submit(Runnable task, T result);
    Future<?> submit(Runnable task);
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
        throws InterruptedException;
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
                                  long timeout, TimeUnit unit)
        throws InterruptedException;
 
    <T> T invokeAny(Collection<? extends Callable<T>> tasks)
        throws InterruptedException, ExecutionException;
    <T> T invokeAny(Collection<? extends Callable<T>> tasks,
                    long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}
复制代码

而ExecutorService又是继承了Executor接口,我们看一下Executor接口的实现:

public interface Executor {
    void execute(Runnable command);
}

我们知道 ThreadPoolExecutor、AbstractExecutorService、ExecutorService和Executor几个之间的关系了。

Executor是一个顶层接口,在它里面只声明了一个方法execute(Runnable),返回值为void,参数为Runnable类型,从字面意思可以理解,就是用来执行传进去的任务的;

然后ExecutorService接口继承了Executor接口,并声明了一些方法:submit、invokeAll、invokeAny以及shutDown等;

抽象类AbstractExecutorService实现了ExecutorService接口,基本实现了ExecutorService中声明的所有方法;

然后ThreadPoolExecutor继承了类AbstractExecutorService。

注意:接下来全是抄的,但我觉得还算蛮重要

深入剖析线程池实现原理总结性内容

在上一节我们从宏观上介绍了ThreadPoolExecutor,下面我们来深入解析一下线程池的具体实现原理,将从下面几个方面讲解:

1.线程池状态

2.任务的执行

3.线程池中的线程初始化

4.任务缓存队列及排队策略

5.任务拒绝策略

6.线程池的关闭

7.线程池容量的动态调整

 

1.线程池状态

在ThreadPoolExecutor中定义了一个volatile变量,另外定义了几个static final变量表示线程池的各个状态:

volatile int runState;
static final int RUNNING    = 0;
static final int SHUTDOWN   = 1;
static final int STOP       = 2;
static final int TERMINATED = 3;

runState表示当前线程池的状态,它是一个volatile变量用来保证线程之间的可见性;

下面的几个static final变量表示runState可能的几个取值。

当创建线程池后,初始时,线程池处于RUNNING状态;

如果调用了shutdown()方法,则线程池处于SHUTDOWN状态,此时线程池不能够接受新的任务,它会等待所有任务执行完毕;

如果调用了shutdownNow()方法,则线程池处于STOP状态,此时线程池不能接受新的任务,并且会去尝试终止正在执行的任务;

当线程池处于SHUTDOWN或STOP状态,并且所有工作线程已经销毁,任务缓存队列已经清空或执行结束后,线程池被设置为TERMINATED状态。

2.任务的执行

在了解将任务提交给线程池到任务执行完毕整个过程之前,我们先来看一下ThreadPoolExecutor类中其他的一些比较重要成员变量:

复制代码
private final BlockingQueue<Runnable> workQueue;              //任务缓存队列,用来存放等待执行的任务
private final ReentrantLock mainLock = new ReentrantLock();   //线程池的主要状态锁,对线程池状态(比如线程池大小
                                                              //、runState等)的改变都要使用这个锁
private final HashSet<Worker> workers = new HashSet<Worker>();  //用来存放工作集
 
private volatile long  keepAliveTime;    //线程存货时间   
private volatile boolean allowCoreThreadTimeOut;   //是否允许为核心线程设置存活时间
private volatile int   corePoolSize;     //核心池的大小(即线程池中的线程数目大于这个参数时,提交的任务会被放进任务缓存队列)
private volatile int   maximumPoolSize;   //线程池最大能容忍的线程数
 
private volatile int   poolSize;       //线程池中当前的线程数
 
private volatile RejectedExecutionHandler handler; //任务拒绝策略
 
private volatile ThreadFactory threadFactory;   //线程工厂,用来创建线程
 
private int largestPoolSize;   //用来记录线程池中曾经出现过的最大线程数
 
private long completedTaskCount;   //用来记录已经执行完毕的任务个数
复制代码

每个变量的作用都已经标明出来了,这里要重点解释一下corePoolSize、maximumPoolSize、largestPoolSize三个变量。

corePoolSize在很多地方被翻译成核心池大小,其实我的理解这个就是线程池的大小

3.线程池中的线程初始化

默认情况下,创建线程池之后,线程池中是没有线程的,需要提交任务之后才会创建线程。

在实际中如果需要线程池创建之后立即创建线程,可以通过以下两个方法办到:

prestartCoreThread():初始化一个核心线程;

prestartAllCoreThreads():初始化所有核心线程

下面是这2个方法的实现:

复制代码
public boolean prestartCoreThread() {
    return addIfUnderCorePoolSize(null); //注意传进去的参数是null
}
 
public int prestartAllCoreThreads() {
    int n = 0;
    while (addIfUnderCorePoolSize(null))//注意传进去的参数是null
        ++n;
    return n;
}
复制代码

注意上面传进去的参数是null,根据第2小节的分析可知如果传进去的参数为null,则最后执行线程会阻塞在getTask方法中的

r = workQueue.take();

即等待任务队列中有任务。

4.任务缓存队列及排队策略

在前面我们多次提到了任务缓存队列,即workQueue,它用来存放等待执行的任务。

workQueue的类型为BlockingQueue<Runnable>,通常可以取下面三种类型:

(1).ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小;

(2).LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;

(3).synchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。

5.任务拒绝策略

当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略:

ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

6.线程池的关闭

ThreadPoolExecutor提供了两个方法,用于线程池的关闭,分别是shutdown()和shutdownNow(),其中:

shutdown():不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务

shutdownNow():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务

7.线程池容量的动态调整

ThreadPoolExecutor提供了动态调整线程池容量大小的方法:setCorePoolSize()和setMaximumPoolSize(),

setCorePoolSize:设置核心池大小

setMaximumPoolSize:设置线程池最大能创建的线程数目大小

当上述参数从小变大时,ThreadPoolExecutor进行线程赋值,还可能立即创建新的线程来执行任务。

使用实例

定制属于自己的非阻塞线程池  

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;


public class CustomThreadPoolExecutor {

private ThreadPoolExecutor pool = null;
public void init() {
pool = new ThreadPoolExecutor(
10,
30,
30,
TimeUnit.MINUTES,
new ArrayBlockingQueue(10),
new CustomThreadFactory(),
new CustomRejectedExecutionHandler());
}

public void destory() {
if(pool != null) {
pool.shutdownNow();
}
}
public ExecutorService getCustomThreadPoolExecutor() {
return this.pool;
}
private class CustomThreadFactory implements ThreadFactory {

private AtomicInteger count = new AtomicInteger(0);
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
String threadName = CustomThreadPoolExecutor.class.getSimpleName() + count.addAndGet(1);
System.out.println(threadName);
t.setName(threadName);
return t;
}
}
private class CustomRejectedExecutionHandler implements RejectedExecutionHandler {

@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
// 记录异常
// 报警处理等
System.out.println("error.............");
}
}
// 测试构造的线程池
public static void main(String[] args) {
CustomThreadPoolExecutor exec = new CustomThreadPoolExecutor();
// 1.初始化
exec.init();
ExecutorService pool = exec.getCustomThreadPoolExecutor();
for(int i=1; i<100; i++) {
System.out.println("提交第" + i + "个任务!");
pool.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("running=====");
}
});
}
// 2.销毁----此处不能销毁,因为任务没有提交执行完,如果销毁线程池,任务也就无法执行了
// exec.destory();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}


方法中建立一个核心线程数为30个,缓冲队列有10个的线程池。每个线程任务,执行时会先睡眠3秒,保证提交10任务时,线程数目被占用完,再提交30任务时,阻塞队列被占用完,,这样提交第41个任务是,会交给CustomRejectedExecutionHandler 异常处理类来处理。

提交任务的代码如下:

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
       
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

注意:41以后提交的任务就不能正常处理了,因为,execute中提交到任务队列是用的offer方法,如上面代码,这个方法是非阻塞的,所以就会交给CustomRejectedExecutionHandler 来处理,所以对于大数据量的任务来说,这种线程池,如果不设置队列长度会OOM,设置队列长度,会有任务得不到处理,接下来我们构建一个阻塞的自定义线程池  

定制属于自己的阻塞线程池  

package com.tongbanjie.trade.test.commons;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class CustomThreadPoolExecutor {  
 
      
    private ThreadPoolExecutor pool = null;  
      
      
     
    public void init() {  
        pool = new ThreadPoolExecutor(  
                1,  
                3,  
                30,  
                TimeUnit.MINUTES,  
                new ArrayBlockingQueue(5),  
                new CustomThreadFactory(),  
                new CustomRejectedExecutionHandler());  
    }  
  
      
    public void destory() {  
        if(pool != null) {  
            pool.shutdownNow();  
        }  
    }  
      
      
    public ExecutorService getCustomThreadPoolExecutor() {  
        return this.pool;  
    }  
      
    private class CustomThreadFactory implements ThreadFactory {  
  
        private AtomicInteger count = new AtomicInteger(0);  
          
        @Override  
        public Thread newThread(Runnable r) {  
            Thread t = new Thread(r);  
            String threadName = CustomThreadPoolExecutor.class.getSimpleName() + count.addAndGet(1);  
            System.out.println(threadName);  
            t.setName(threadName);  
            return t;  
        }  
    }  
      
      
    private class CustomRejectedExecutionHandler implements RejectedExecutionHandler {  
  
        @Override  
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {  
        try {
                                // 核心改造点,由blockingqueue的offer改成put阻塞方法
executor.getQueue().put(r);
} catch (InterruptedException e) {
e.printStackTrace();
}
        }  
    }  
      
      
      
    // 测试构造的线程池  
    public static void main(String[] args) {  
   
        CustomThreadPoolExecutor exec = new CustomThreadPoolExecutor();  
        // 1.初始化  
        exec.init();  
          
        ExecutorService pool = exec.getCustomThreadPoolExecutor();  
        for(int i=1; i<100; i++) {  
            System.out.println("提交第" + i + "个任务!");  
            pool.execute(new Runnable() {  
                @Override  
                public void run() {  
                    try {  
                    System.out.println(">>>task is running====="); 
                        TimeUnit.SECONDS.sleep(10);
                    } catch (InterruptedException e) {  
                        e.printStackTrace();  
                    }  
                }  
            });  
        }  
          
          
        // 2.销毁----此处不能销毁,因为任务没有提交执行完,如果销毁线程池,任务也就无法执行了  
        // exec.destory();  
          
        try {  
            Thread.sleep(10000);  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
    }  
}  
解释:当提交任务被拒绝时,进入拒绝机制,我们实现拒绝方法,把任务重新用阻塞提交方法put提交,实现阻塞提交任务功能,防止队列过大,OOM,提交被拒绝方法在下面  

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();

        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            // 进入拒绝机制, 我们把runnable任务拿出来,重新用阻塞操作put,来实现提交阻塞功能
            reject(command);
    }




参考: http://blog.sina.com.cn/s/blog_9b7d76e30102wlz8.html
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值