java线程池

来源:互联网 发布:之二虫又何知 编辑:程序博客网 时间:2024/06/10 19:54

转载地址:http://blog.csdn.net/mazhimazh/article/details/19243889


线程池的框架图如下:




1、Executor任务提交接口与Executors工具类

Executor框架同java.util.concurrent.Executor接口在jdk5.0中被引入,Executor框架是一个根据一组执行策略调用,执行异步任务的框架。Executor存在的目的是提供一种将“任务提交”和"任务如何执行"分离开来的机制。定义如下:


public interface Executor {    /**     * Executes the given command at some time in the future.  The command     * may execute in a new thread, in a pooled thread, or in the calling     * thread, at the discretion of the <tt>Executor</tt> implementation.     *     * @param command the runnable task     * @throws RejectedExecutionException if this task cannot be     * accepted for execution.     * @throws NullPointerException if command is null     */    void execute(Runnable command);}

虽然只有一个方法,但是为强大且灵活的异步任务执行框架提供了基础。它提供了一种方法将任务的提交过程和执行过程解耦开来。并用runnable表示任务,那么我们如何得到Executor对象呢?这就是接下来要介绍的Executors对象了。

Executors为Executor、ExecutorService、SchduledExecutorService、ThreadFactory和Callable类提供了一些工具方法。Executors可以方便的创建线程池。

1、newCachThreadPool:该线程池比较适合没有固定大小且比较快速就能完成的小任务。它将为每个任务创建一个线程。如果是这样子那它和直接创建线程有什么区别的呢?

看到它的第三个参数60L和第四个参数TimeUnit.SECONDE了吗?这就是关键所在,这两个参数的意思是60秒内可以复用已经创建的线程,即如果一个线程执行完任务,它会存活60S,如果这个期间没用新的任务被提交,那么这个线程会销毁。

下面是Executors中的newCachThreadPool()方法的源码:

  /**     * Creates a thread pool that creates new threads as needed, but     * will reuse previously constructed threads when they are     * available.  These pools will typically improve the performance     * of programs that execute many short-lived asynchronous tasks.     * Calls to <tt>execute</tt> will reuse previously constructed     * threads if available. If no existing thread is available, a new     * thread will be created and added to the pool. Threads that have     * not been used for sixty seconds are terminated and removed from     * the cache. Thus, a pool that remains idle for long enough will     * not consume any resources. Note that pools with similar     * properties but different details (for example, timeout parameters)     * may be created using {@link ThreadPoolExecutor} constructors.     *     * @return the newly created thread pool     */    public static ExecutorService newCachedThreadPool() {        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,  60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());    }


2、newFixedThreadPool:使用的Thread对象的数量是有限的。如果提交的任务数量大于最大任务数。那么这些任务将排队。如果有一个线程,任务执行完,那么它将根据调度策略等待执行下一个任务。下面是Executors 中newFixedThreadPool的源码:

    /**     * Creates a thread pool that reuses a fixed number of threads     * operating off a shared unbounded queue.  At any point, at most     * <tt>nThreads</tt> threads will be active processing tasks.     * If additional tasks are submitted when all threads are active,     * they will wait in the queue until a thread is available.     * If any thread terminates due to a failure during execution     * prior to shutdown, a new one will take its place if needed to     * execute subsequent tasks.  The threads in the pool will exist     * until it is explicitly {@link ExecutorService#shutdown shutdown}.     *     * @param nThreads the number of threads in the pool     * @return the newly created thread pool     * @throws IllegalArgumentException if {@code nThreads <= 0}     */    public static ExecutorService newFixedThreadPool(int nThreads) {        return new ThreadPoolExecutor(nThreads, nThreads,  0L, TimeUnit.MILLISECONDS,  new LinkedBlockingQueue<Runnable>());    }


3、newSingleThreadExecutor:就是线程数量为1的fixedThreadPool,如果提交了多个任务,那么这些任务将会排队。每一个任务都会在下一个任务执行之前运行结束。所有的任务都会使用相同的线程。下面是Executors中的newSingelThreadExecutor的源码:


    /**     * Creates an Executor that uses a single worker thread operating     * off an unbounded queue. (Note however that if this single     * thread terminates due to a failure during execution prior to     * shutdown, a new one will take its place if needed to execute     * subsequent tasks.)  Tasks are guaranteed to execute     * sequentially, and no more than one task will be active at any     * given time. Unlike the otherwise equivalent     * <tt>newFixedThreadPool(1)</tt> the returned executor is     * guaranteed not to be reconfigurable to use additional threads.     *     * @return the newly created single-threaded Executor     */    public static ExecutorService newSingleThreadExecutor() {        return new FinalizableDelegatedExecutorService            (new ThreadPoolExecutor(1, 1,  0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));    }

4、newScheduledThreadPool:创建一个固定长度的线程池。而且以延迟或者定时的方式来执行任务。


 /**     * Creates a new {@code ScheduledThreadPoolExecutor} with the     * given core pool size.     *     * @param corePoolSize the number of threads to keep in the pool, even     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set     * @throws IllegalArgumentException if {@code corePoolSize < 0}     */    public ScheduledThreadPoolExecutor(int corePoolSize) {        super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,              new DelayedWorkQueue());    }



通过前面的线程池的源码可以知道:

1、除了CachedThreadPool使用的是直接提交策略的队列以外。其余两个都是采用的无界缓冲队列。也就是说FixedThreadPool和SingleThreadPool创建的线程数不会大于corePoolSize;


2、三个线程池采用的ThreadPoolExecutor构造方法都是同一个,使用的都是默认的ThreadFactory和handler。


  /**     * Creates a new {@code ThreadPoolExecutor} with the given initial     * parameters and default thread factory and rejected execution handler.     * It may be more convenient to use one of the {@link Executors} factory     * methods instead of this general purpose constructor.     *     * @param corePoolSize the number of threads to keep in the pool, even     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set     * @param maximumPoolSize the maximum number of threads to allow in the     *        pool     * @param keepAliveTime when the number of threads is greater than     *        the core, this is the maximum time that excess idle threads     *        will wait for new tasks before terminating.     * @param unit the time unit for the {@code keepAliveTime} argument     * @param workQueue the queue to use for holding tasks before they are     *        executed.  This queue will hold only the {@code Runnable}     *        tasks submitted by the {@code execute} method.     * @throws IllegalArgumentException if one of the following holds:<br>     *         {@code corePoolSize < 0}<br>     *         {@code keepAliveTime < 0}<br>     *         {@code maximumPoolSize <= 0}<br>     *         {@code maximumPoolSize < corePoolSize}     * @throws NullPointerException if {@code workQueue} is null     */    public ThreadPoolExecutor(int corePoolSize,                              int maximumPoolSize,                              long keepAliveTime,                              TimeUnit unit,                              BlockingQueue<Runnable> workQueue) {        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,             Executors.defaultThreadFactory(), defaultHandler);    }


也就是说三个线程池创建的线程对象都是同组对象。


2、ExecutorService任务周期管理接口

Executor的实现通常都会创建线程来处理任务,由于使用异步方式来执行任务时,由于之前提交的任务的状态是不能立即可以见的,如果想关闭应用程序的话,就要把受影响的任务反馈给应用程序。


为了解决执行任务的声明周期问题,ExecutorService扩展了Executor接口,添加了一个用于声明周期的管理的方法:


public interface ExecutorService extends Executor {      void shutdown();      List<Runnable> shutdownNow();      boolean isShutdown();      boolean isTerminated();      boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;      // 省略部分方法  }  

3、ThreadPoolExecutor 线程池实现类:

先看下定义的重要变量:

private final BlockingQueue<Runnable> workQueue;              // 阻塞队列  private final ReentrantLock mainLock = new ReentrantLock();   // 互斥锁  private final HashSet<Worker> workers = new HashSet<Worker>();// 线程集合.一个Worker对应一个线程  private final Condition termination = mainLock.newCondition();// 终止条件  private int largestPoolSize;           // 线程池中线程数量曾经达到过的最大值。  private long completedTaskCount;       // 已完成任务数量  private volatile ThreadFactory threadFactory;     // ThreadFactory对象,用于创建线程。  private volatile RejectedExecutionHandler handler;// 拒绝策略的处理句柄  private volatile long keepAliveTime;   // 线程池维护线程所允许的空闲时间  private volatile boolean allowCoreThreadTimeOut;  private volatile int corePoolSize;     // 线程池维护线程的最小数量,哪怕是空闲的  private volatile int maximumPoolSize;  // 线程池维护的最大线程数量  

其中有几个重要的规则要说明下:

1、corePoolSize和maximumPoolSize :

由于ThreadPoolExecutor将根据corePoolSize和maximumPoolSize设置的边界自动调整池大小。当新的任务在方法execute(java.lang.Runable)总提交时会有如下的情况:

1、如果运行的线程少于corePoolSize的时候,则创建新线程来处理请求。即使其他辅助线程是空的。

2、如果设置的corePoolSize和maximumPoolSize相同,则创建的线程池大小是固定的。如果运行的线程和corePoolSize相同,当有新的请求过来时,若workQueue未满,则将请求放入workQueue中,等待有空闲的线程去workQueue中取任务处理。

3、如果运行的线程多于corePoolSize而少于maximumPoolSize,仅当队列满时才创建新的线程去处理请求。

4、如果运行的线程多于corepoolsize而等于maximumPoolSize,并且队列已经满了,则通过handler所指定的策略来处理新请求。

5、如果将maximumPoolSize的值,设置为无界值(Integer.MAX_VALUE),则允许池适应任意数量的并发任务。

也就是说,处理任务的优先级:

1、核心线程corePoolSize > 任务队列workQueue > 最大线程数maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。

2、当线程池中的线程多于corePoolSize的时候,多于的线程会存货keepaliveTime,如果无请求可处理就自行销毁。

2、workQueue

线程池所使用的缓冲队列,该缓冲队列的长度决定了能够缓冲的最大数量,缓冲队列有三种通用策略:

1、直接提交

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

2、无界队列

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

3、有界队列

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

3、ThreadFactory

使用 ThreadFactory 创建新线程。如果没有另外说明,则在同一个 ThreadGroup 中一律使用 Executors.defaultThreadFactory() 创建线程,并且这些线程具有相同的 NORM_PRIORITY 优先级和非守护进程状态。通过提供不同的 ThreadFactory,可以改变线程的名称、线程组、优先级、守护进程状态等等。如果从 newThread 返回 null 时 ThreadFactory 未能创建线程,则执行程序将继续运行,但不能执行任何任务。


public interface ThreadFactory {      Thread newThread(Runnable r);  }  


而构造方法中的threadFactory对象,是通过 Executors.defaultThreadFactory()返回的。Executors.java中的defaultThreadFactory()源码如下:

public static ThreadFactory defaultThreadFactory() {       return new DefaultThreadFactory();   }  


static class DefaultThreadFactory implements ThreadFactory {      private static final AtomicInteger poolNumber = new AtomicInteger(1);      private final ThreadGroup group;      private final AtomicInteger threadNumber = new AtomicInteger(1);      private final String namePrefix;        DefaultThreadFactory() {          SecurityManager s = System.getSecurityManager();          group = (s != null) ? s.getThreadGroup() :  Thread.currentThread().getThreadGroup();          namePrefix = "pool-" +  poolNumber.getAndIncrement() +  "-thread-";      }      // 为线程池创建新的任务执行线程      public Thread newThread(Runnable r) {          // 线程对应的任务是Runnable对象r          Thread t = new Thread(group, r,namePrefix + threadNumber.getAndIncrement(), 0);          // 设为非守护线程          if (t.isDaemon())              t.setDaemon(false);          // 将优先级设为Thread.NORM_PRIORITY          if (t.getPriority() != Thread.NORM_PRIORITY)              t.setPriority(Thread.NORM_PRIORITY);          return t;      }  } 


4、RejectedExecutionHandler

 当Executor已经关闭(即执行了executorService.shutdown()方法后),并且Executor将有限边界用于最大线程和工作队列容量,且已经饱和时,在方法execute()中提交的新任务将被拒绝.


在以上述情况下,execute 方法将调用其 RejectedExecutionHandler 的 RejectedExecutionHandler.rejectedExecution(java.lang.Runnable, java.util.concurrent.ThreadPoolExecutor) 方法。下面提供了四种预定义的处理程序策略:

1) 在默认的 ThreadPoolExecutor.AbortPolicy      处理程序遭到拒绝将抛出运行时 RejectedExecutionException;

2) 在 ThreadPoolExecutor.CallerRunsPolicy        线程调用运行该任务的 execute 本身。此策略提供简单的反馈控制机制,能够减缓新任务的提交速度

3) 在 ThreadPoolExecutor.DiscardPolicy             不能执行的任务将被删除;

4) 在 ThreadPoolExecutor.DiscardOldestPolicy    如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程)。


线程池默认采用的是defaultHandler策略,首先看defaulthandler的定义

private static final RejectedExecutionHandler defaultHandler = new AbortPolicy(); // 使用默认的拒绝策略  

public static class AbortPolicy implements RejectedExecutionHandler {      public AbortPolicy() { }      // 抛出异常      public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {          throw new RejectedExecutionException("Task " + r.toString() +  " rejected from " +  e.toString());      }  } 

下面看下其他拒绝策略的具体实现:

class MyRunnable implements Runnable {      private String name;      public MyRunnable(String name) {          this.name = name;      }      @Override      public void run() {          try {              System.out.println(this.name + " is running.");              Thread.sleep(100);          } catch (Exception e) {              e.printStackTrace();          }      }  } 


如上是一个测试任务的例子,下面编写4个测试用例来测试。

1、DiscardPolicy、示例

public class DiscardPolicyDemo {private static final int THREADS_SIZE = 1;  private static final int CAPACITY = 1;  public static void main(String[] args) {//创建线程池,线程池ThreadPoolExecutor pool = new ThreadPoolExecutor(THREADS_SIZE, THREADS_SIZE, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(CAPACITY));//设置线程池的拒绝策略为丢弃策略pool.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());for (int i = 0; i < 10; i++) {MyRunnable mr = new MyRunnable("task_" + i);pool.execute(mr);}pool.shutdown();}        }

运行结果:

task_0 is running.task_1 is running.



线程池的“最大线程池大小”和“线程池核心大小”都是1(THREADS_SIZE),这意味着线程池能同时运行的任务数量最大只能是1。

线程池pool的阻塞队列是ArrayBlockQueue,ArrayBlockQueue是一个有界阻塞队列。arrayBlockQueue的容量是1,这就意味着,线程池的阻塞队列只能有一个线程阻塞等待。

通过运行结果可以知道,线程池中运行了2个任务,第一个任务是直接放到workQueue中,通过线程去执行,第2个任务放到阻塞队列中等待,。其他的任务都被丢弃了。

2、DiscardOldestPolicy 示例



public class DiscardOldestPolicyDemo {private static final int THREADS_SIZE = 1;  private static final int CAPACITY = 1;  public static void main(String[] args) {//创建线程池,线程池ThreadPoolExecutor pool = new ThreadPoolExecutor(THREADS_SIZE, THREADS_SIZE, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(CAPACITY));//设置线程池的拒绝策略为丢弃策略pool.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());for (int i = 0; i < 10; i++) {MyRunnable mr = new MyRunnable("task_" + i);pool.execute(mr);}pool.shutdown();}}

运行结果:

task_0 is running.task_9 is running.


将线程池的拒绝策略,由DiscardPolicy修改为DiscardOldestPolicy后,当有任务添加到线程池被拒绝时,线程会丢弃阻塞队列中末尾的任务,然后将被拒绝的任务添加到队尾。



3、AbortPolicy 示例

public class AbortPolicyDemo {private static final int THREADS_SIZE = 1;  private static final int CAPACITY = 2;  public static void main(String[] args) {//创建线程池,线程池ThreadPoolExecutor pool = new ThreadPoolExecutor(THREADS_SIZE, THREADS_SIZE, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(CAPACITY));//设置线程池的拒绝策略为丢弃策略pool.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());for (int i = 0; i < 10; i++) {MyRunnable mr = new MyRunnable("task_" + i);pool.execute(mr);}pool.shutdown();}}


某一次运行结果:

Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task com.threadpool.kw.MyRunnable@3dc0bb rejected from java.util.concurrent.ThreadPoolExecutor@384065[Running, pool size = 1, active threads = 1, queued tasks = 2, completed tasks = 0]at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(Unknown Source)at java.util.concurrent.ThreadPoolExecutor.reject(Unknown Source)at java.util.concurrent.ThreadPoolExecutor.execute(Unknown Source)at com.threadpool.kw.AbortPolicyDemo.main(AbortPolicyDemo.java:25)task_0 is running.task_1 is running.task_2 is running.


当线程池的拒绝策略为AbortPolicy的时候,当线程池中有任务添加被拒绝的时候,会抛出异常。


4、CallerRunPolicy      示例

public class CallerRunPolicyDemo {private static final int THREADS_SIZE = 1;  private static final int CAPACITY = 1;  public static void main(String[] args) {//创建线程池,线程池ThreadPoolExecutor pool = new ThreadPoolExecutor(THREADS_SIZE, THREADS_SIZE, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(CAPACITY));//设置线程池的拒绝策略为丢弃策略pool.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());for (int i = 0; i < 10; i++) {MyRunnable mr = new MyRunnable("task_" + i);pool.execute(mr);}pool.shutdown();}}


当线程池的策略为CallerRunPolicy的时候,当任务添加到线程池被拒绝的时候,线程池会把拒绝的任务放到正在运行的线程池中去运行。


1 0
原创粉丝点击