Java 专栏

Java 线程池(Executor)

2017-08-28  本文已影响6人  _凌浩雨

Java 线程池(Executor)

  1. ThreadPoolExecutor使用

/**
* ThreadPoolExecutor 使用测试类
* 在java doc中,并不提倡我们直接使用ThreadPoolExecutor,
* 而是使用Executors类中提供的几个静态方法来创建线程池
* @author mazaiting
*/
public class ThreadPoolExecutorTest {
   
   /**
    * 当前类主方法入口,在主函数中静态调用即可
    */
   public static void exec() {
       // 实例化线程池
       // corePoolSize : 核心池的大小(即线程池中的线程数目大于这个参数时,提交的任务会被放进任务缓存队列)
       // maximumPoolSize : 线程池最大能容忍的线程数
       // keepAliveTime : 线程存活时间
       // unit : 参数keepAliveTime的时间单位
       // workQueue : 任务缓存队列,用来存放等待执行的任务
       ThreadPoolExecutor executor = new ThreadPoolExecutor(
               5, 10, 200, TimeUnit.MILLISECONDS, 
               new ArrayBlockingQueue<Runnable>(5));
       for (int i = 0; i < 15; i++) {
           MyTask task = new MyTask(i);
           executor.execute(task); // 执行任务
           System.out.println("线程池中线程数目:"+executor.getPoolSize()+",队列中等待执行的任务数目:"+
                    executor.getQueue().size()+",已执行玩别的任务数目:"+executor.getCompletedTaskCount());
       }
       // 不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务
       executor.shutdown();
   }
   
   static class MyTask implements Runnable{
       private int taskNum;
       
       public MyTask(int num) {
           this.taskNum = num;
       }
       
       public void run() {
           System.out.println("正在执行task: " + taskNum);
           try {
               // 当前线程睡眠4秒
               Thread.currentThread().sleep(4000);
           } catch (InterruptedException e) {}
           System.out.println("task: " + taskNum + "执行完毕");
       }
       
   }
}

执行结果:

正在执行task: 0
线程池中线程数目:1,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
线程池中线程数目:2,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
正在执行task: 1
正在执行task: 2
线程池中线程数目:3,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
线程池中线程数目:4,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
正在执行task: 3
线程池中线程数目:5,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:1,已执行玩别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:2,已执行玩别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:3,已执行玩别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:4,已执行玩别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:5,已执行玩别的任务数目:0
正在执行task: 4
线程池中线程数目:6,队列中等待执行的任务数目:5,已执行玩别的任务数目:0
正在执行task: 10
线程池中线程数目:7,队列中等待执行的任务数目:5,已执行玩别的任务数目:0
正在执行task: 11
线程池中线程数目:8,队列中等待执行的任务数目:5,已执行玩别的任务数目:0
正在执行task: 12
线程池中线程数目:9,队列中等待执行的任务数目:5,已执行玩别的任务数目:0
正在执行task: 13
线程池中线程数目:10,队列中等待执行的任务数目:5,已执行玩别的任务数目:0
正在执行task: 14
task: 1执行完毕
task: 0执行完毕
task: 2执行完毕
正在执行task: 5
正在执行task: 6
task: 4执行完毕
正在执行task: 8
task: 3执行完毕
正在执行task: 9
task: 12执行完毕
task: 13执行完毕
正在执行task: 7
task: 10执行完毕
task: 11执行完毕
task: 14执行完毕
task: 5执行完毕
task: 9执行完毕
task: 6执行完毕
task: 8执行完毕
task: 7执行完毕
  1. Executors使用

/**
 * 线程池ExecutorService测试类
 * @author mazaiting
 */
public class ExecutorsTest {

    public static void exec() {
        /**
         * CachedThreadPool会创建一个缓存区,将初始化的线程缓存起来。会终止并且从缓存中移除已有60秒未被使用的线程。
         *  如果线程有可用的,就使用之前创建好的线程,
         *  如果线程没有可用的,就新创建线程。
         *      
         *  重用:缓存型池子,先查看池中有没有以前建立的线程,如果有,就reuse;如果没有,就建一个新的线程加入池中
         *  使用场景:缓存型池子通常用于执行一些生存期很短的异步型任务,因此在一些面向连接的daemon型SERVER中用得不多。
         *  超时:能reuse的线程,必须是timeout IDLE内的池中线程,缺省timeout是60s,超过这个IDLE时长,线程实例将被终止及移出池。
         *  结束:注意,放入CachedThreadPool的线程不必担心其结束,超过TIMEOUT不活动,其会自动被终止。
         */
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        
        /**
         * 在FixedThreadPool中,有一个固定大小的池。
         *  如果当前需要执行的任务超过池大小,那么多出的任务处于等待状态,直到有空闲下来的线程执行任务,
         *  如果当前需要执行的任务小于池大小,空闲的线程也不会去销毁。
         *  
         *  重用:fixedThreadPool与cacheThreadPool差不多,也是能reuse就用,但不能随时建新的线程
         *  固定数目:其独特之处在于,任意时间点,最多只能有固定数目的活动线程存在,此时如果有新的线程要建立,只能放在另外的队列中等待,直到当前的线程中某个线程终止直接被移出池子
         *  超时:和cacheThreadPool不同,FixedThreadPool没有IDLE机制(可能也有,但既然文档没提,肯定非常长,类似依赖上层的TCP或UDP IDLE机制之类的),
         *  使用场景:所以FixedThreadPool多数针对一些很稳定很固定的正规并发线程,多用于服务器
         *  源码分析:从方法的源代码看,cache池和fixed 池调用的是同一个底层池,只不过参数不同:
         *  fixed池线程数固定,并且是0秒IDLE(无IDLE)
         *  cache池线程数支持0-Integer.MAX_VALUE(显然完全没考虑主机的资源承受能力),60秒IDLE
         */
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
        
        /**
         * ScheduledThreadPool是一个固定大小的线程池,与FixedThreadPool类似,执行的任务是定时执行。
         */
        ExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
        
        /**
         * SingleThreadExecutor得到的是一个单个的线程,这个线程会保证你的任务执行完成。
         * 如果当前线程意外终止,会创建一个新线程继续执行任务,这和我们直接创建线程不同,也和newFixedThreadPool(1)不同。
         */
        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
        
        /**
         * 创建一个单线程执行器,它可以调度命令在给定的延迟之后运行,或者定期执行。(注意,如果此单线程由于在关闭前执行失败而终止,
         * 则新线程将在需要执行后续任务时取代其位置。)任务可以保证按顺序执行,在任何给定的时间内,不超过一个任务是活动的。
         * 与其他等效的{ @ code newScheduledThreadPool(1)}不同的是,返回的executor不能被重新配置以使用额外的线程。
         */
        ExecutorService singleThreadScheduledExecutor = Executors.newSingleThreadScheduledExecutor();
        
        /**
         * 创建一个系统所能容纳的线程池大小(Runtime.getRuntime().availableProcessors())
         * 作为并行级别
         */
        ExecutorService workStealingPool = Executors.newWorkStealingPool();
        
        run(cachedThreadPool);
//      run(fixedThreadPool);
//      run(scheduledThreadPool);
//      run(singleThreadExecutor);
//      run(singleThreadScheduledExecutor);
//      run(workStealingPool);
    }   
    
    /**
     * 执行任务
     * @param cachedThreadPool 线程池服务
     */
    private static void run(ExecutorService threadPool) {
        for (int i = 0; i < 5; i++) {
            final int taskID = i;
            threadPool.execute(new Runnable() {
                
                public void run() {
                    for (int j = 0; j < 5; j++) {
                        try {
                            // 为了测试出效果,让每次任务执行都需要一定时间   
                            Thread.currentThread().sleep(20);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("第" + taskID + "次任务的第" + j + "次执行");
                    }
                }
            });
        }
        // 不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务
        threadPool.shutdown();
    }
    
}

run(cachedThreadPool)执行结果如下:

第4次任务的第0次执行
第1次任务的第0次执行
第0次任务的第0次执行
第3次任务的第0次执行
第2次任务的第0次执行
第0次任务的第1次执行
第4次任务的第1次执行
第2次任务的第1次执行
第3次任务的第1次执行
第1次任务的第1次执行
第1次任务的第2次执行
第0次任务的第2次执行
第2次任务的第2次执行
第4次任务的第2次执行
第3次任务的第2次执行
第1次任务的第3次执行
第4次任务的第3次执行
第0次任务的第3次执行
第3次任务的第3次执行
第2次任务的第3次执行
第1次任务的第4次执行
第4次任务的第4次执行
第2次任务的第4次执行
第0次任务的第4次执行
第3次任务的第4次执行

run(fixedThreadPool)执行结果如下:

第2次任务的第0次执行
第1次任务的第0次执行
第0次任务的第0次执行
第2次任务的第1次执行
第1次任务的第1次执行
第0次任务的第1次执行
第1次任务的第2次执行
第0次任务的第2次执行
第2次任务的第2次执行
第1次任务的第3次执行
第0次任务的第3次执行
第2次任务的第3次执行
第1次任务的第4次执行
第0次任务的第4次执行
第2次任务的第4次执行
第3次任务的第0次执行
第4次任务的第0次执行
第3次任务的第1次执行
第4次任务的第1次执行
第3次任务的第2次执行
第4次任务的第2次执行
第4次任务的第3次执行
第3次任务的第3次执行
第3次任务的第4次执行
第4次任务的第4次执行

run(scheduledThreadPool)执行结果如下:

第2次任务的第0次执行
第1次任务的第0次执行
第0次任务的第0次执行
第0次任务的第1次执行
第2次任务的第1次执行
第1次任务的第1次执行
第1次任务的第2次执行
第2次任务的第2次执行
第0次任务的第2次执行
第2次任务的第3次执行
第0次任务的第3次执行
第1次任务的第3次执行
第2次任务的第4次执行
第1次任务的第4次执行
第0次任务的第4次执行
第3次任务的第0次执行
第4次任务的第0次执行
第3次任务的第1次执行
第4次任务的第1次执行
第3次任务的第2次执行
第4次任务的第2次执行
第3次任务的第3次执行
第4次任务的第3次执行
第3次任务的第4次执行
第4次任务的第4次执行

run(singleThreadExecutor)执行结果如下:

第0次任务的第0次执行
第0次任务的第1次执行
第0次任务的第2次执行
第0次任务的第3次执行
第0次任务的第4次执行
第1次任务的第0次执行
第1次任务的第1次执行
第1次任务的第2次执行
第1次任务的第3次执行
第1次任务的第4次执行
第2次任务的第0次执行
第2次任务的第1次执行
第2次任务的第2次执行
第2次任务的第3次执行
第2次任务的第4次执行
第3次任务的第0次执行
第3次任务的第1次执行
第3次任务的第2次执行
第3次任务的第3次执行
第3次任务的第4次执行
第4次任务的第0次执行
第4次任务的第1次执行
第4次任务的第2次执行
第4次任务的第3次执行
第4次任务的第4次执行

run(singleThreadScheduledExecutor)执行结果如下:

第0次任务的第0次执行
第0次任务的第1次执行
第0次任务的第2次执行
第0次任务的第3次执行
第0次任务的第4次执行
第1次任务的第0次执行
第1次任务的第1次执行
第1次任务的第2次执行
第1次任务的第3次执行
第1次任务的第4次执行
第2次任务的第0次执行
第2次任务的第1次执行
第2次任务的第2次执行
第2次任务的第3次执行
第2次任务的第4次执行
第3次任务的第0次执行
第3次任务的第1次执行
第3次任务的第2次执行
第3次任务的第3次执行
第3次任务的第4次执行
第4次任务的第0次执行
第4次任务的第1次执行
第4次任务的第2次执行
第4次任务的第3次执行
第4次任务的第4次执行

run(workStealingPool)未执行出结果,从Java 1.8开始存在

示例代码下载

上一篇下一篇

猜你喜欢

热点阅读