美文网首页线程池线程池
线程池--方便的创建和使用:Executors工具类

线程池--方便的创建和使用:Executors工具类

作者: 颤抖的闪电 | 来源:发表于2017-03-11 15:57 被阅读939次

    Executors为Executor,ExecutorService,ScheduledExecutorService,ThreadFactory和Callable类提供了一些工具方法,类似于集合中的Collections类的功能。


    生成线程池采用了工具类Executors的静态方法,以下是几种常见的线程池。
      1>newCachedThreadPool :该线程池比较适合没有固定大小并且比较快速就能完成的小任务,它将为每个任务创建一个线程。那这样子它与直接创建线程对象(new Thread())有什么区别呢?看到它的第三个参数60L和第四个参数TimeUnit.SECONDS了吗?好处就在于60秒内能够重用已创建的线程。
    CachedThreadPool:无界线程池,可以进行自动线程回收。
    下面是Executors中的newCachedThreadPool()的源代码:
    public static ExecutorService newCachedThreadPool() {  
            return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());  
        }  
    

    如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。SynchronousQueue是一个是缓冲区为1的阻塞队列。
    示例代码如下:

    packagetest;
    importjava.util.concurrent.ExecutorService;
    importjava.util.concurrent.Executors;
     public class ThreadPoolExecutorTest{
            public static void main(String[]args){
                ExecutorService cachedThreadPool= Executors.newCachedThreadPool();
                for(int i=0;i<10;i++){
                    final int index=i;
                    try{
                        Thread.sleep(index*1000);
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                    cachedThreadPool.execute(new Runnable(){
                        public void run(){
                            System.out.println(index);
                        }
                    });
                }
            }
        }
    //线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。
    

    2> newFixedThreadPool使用的Thread对象的数量是有限的,如果提交的任务数量大于限制的最大线程数,那么这些任务讲排队,然后当有一个线程的任务结束之后,将会根据调度策略继续等待执行下一个任务。
    FixedThreadPool:只有核心线程的线程池,大小固定 (其缓冲队列是无界的) 。
    下面是Executors中的newFixedThreadPool()的源代码:

    public static ExecutorService newFixedThreadPool(int nThreads) {  
          return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());  
      }  
    

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

    package test;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    public class ThreadPoolExecutorTest {
     public static void main(String[] args) {
      ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
      for (int i = 0; i < 10; i++) {
       final int index = i;
       fixedThreadPool.execute(new Runnable() {
        public void run() {
         try {
          System.out.println(index);
          Thread.sleep(2000);
         } catch (InterruptedException e) {
          e.printStackTrace();
         }
        }
       });
      }
     }
    }
    //因为线程池大小为3,每个任务输出index后sleep 2秒,所以每两秒打印3个数字。
    //定长线程池的大小最好根据[系统](http://www.2cto.com/os/)资源进行设置。如Runtime.getRuntime().availableProcessors()
    

    3>newSingleThreadExecutor就是线程数量为1的FixedThreadPool,如果提交了多个任务,那么这些任务将会排队,每个任务都会在下一个任务开始之前运行结束,所有的任务将会使用相同的线程。
    SingleThreadExecutor:单个后台线程 (其缓冲队列是无界的)
    下面是Executors中的newSingleThreadExecutor()的源代码:

    public static ExecutorService newSingleThreadExecutor() {  
            return new FinalizableDelegatedExecutorService  
                (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));  
        }  
    

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

    package test;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    public class ThreadPoolExecutorTest {
     public static void main(String[] args) {
      ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
      for (int i = 0; i < 10; i++) {
       final int index = i;
       singleThreadExecutor.execute(new Runnable() {
        public void run() {
         try {
          System.out.println(index);
          Thread.sleep(2000);
         } catch (InterruptedException e) {
          e.printStackTrace();
         }
        }
       });
      }
     }
    }
    //结果依次输出,相当于顺序执行各个任务。
    

    4>newScheduledThreadPool创建一个固定长度的线程池,而且以延迟或定时的方式来执行任务。
    ScheduledThreadPool:核心线程池固定,大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

    public static ExecutorService newScheduledThreadPool(int corePoolSize) {         
        return new ScheduledThreadPool(corePoolSize, 
                  Integer.MAX_VALUE,                                                  
                  DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,                                                    
                  new DelayedWorkQueue());    
    }
    

    创建一个周期性执行任务的线程池。如果闲置,非核心线程池会在DEFAULT_KEEPALIVEMILLIS时间内回收。
    示例代码如下:

    package test;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    public class ThreadPoolExecutorTest {
     public static void main(String[] args) {
      ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
      scheduledThreadPool.schedule(new Runnable() {
       public void run() {
        System.out.println("delay 3 seconds");
       }
      }, 3, TimeUnit.SECONDS);
     }
    }
    //表示延迟3秒执行。
    

    定期执行示例代码如下:

    package test;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    public class ThreadPoolExecutorTest {
     public static void main(String[] args) {
      ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
      scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
       public void run() {
        System.out.println("delay 1 seconds, and excute every 3 seconds");
       }
      }, 1, 3, TimeUnit.SECONDS);
     }
    }
    //表示延迟1秒后每3秒执行一次。
    

    通过如上配置的线程池的创建方法源代码,我们可以发现:
      1、 除了CachedThreadPool使用的是直接提交策略的缓冲队列以外,其余两个用的采用的都是无界缓冲队列,也就说,FixedThreadPool和SingleThreadExecutor创建的线程数量就不会超过 corePoolSize。
       2、我们可以再来看看三个线程池采用的ThreadPoolExecutor构造方法都是同一个,使用的都是默认的ThreadFactory和handler:

     private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();  
       
     public ThreadPoolExecutor(int corePoolSize,  
                         int maximumPoolSize,  
                         long keepAliveTime,  
                         TimeUnit unit,  
                         BlockingQueue<Runnable> workQueue) {  
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,  
            Executors.defaultThreadFactory(), defaultHandler);  
    }  
    

    也就说三个线程池创建的线程对象都是同组,优先权等级为正常的Thread.NORM_PRIORITY(5)的非守护线程,使用的被拒绝任务处理方式是直接抛出异常的AbortPolicy策略。

    线程池最常用的提交任务的方法有两种:
    execute:

    ExecutorService.execute(Runnable runable);
    

    submit:

    FutureTask task = ExecutorService.submit(Runnable runnable);
    
    FutureTask<T> task = ExecutorService.submit(Runnable runnable,T Result);
    
    FutureTask<T> task = ExecutorService.submit(Callable<T> callable);
    

    submit(Callable callable)的实现,submit(Runnable runnable)同理。

    public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        FutureTask<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }
    

    可以看出submit开启的是有返回结果的任务,会返回一个FutureTask对象,这样就能通过get()方法得到结果。submit最终调用的也是execute(Runnable runable),submit只是将Callable对象或Runnable封装成一个FutureTask对象,因为FutureTask是个Runnable,所以可以在execute中执行。关于Callable对象和Runnable怎么封装成FutureTask对象,见Callable和Future、FutureTask的使用

    参考来自http://blog.csdn.net/zhoufenqin/article/details/51012666
    参考来自http://blog.csdn.net/he90227/article/details/52576452
    参考来自http://www.2cto.com/kf/201606/517766.html

    相关文章

      网友评论

        本文标题:线程池--方便的创建和使用:Executors工具类

        本文链接:https://www.haomeiwen.com/subject/awaagttx.html