线程池ThreadPoolExecutor小记

ThreadPoolExecutor:jdk中的基础线程池之一。jdk中不具有定时执行功能的线程池(FixedThreadPool,SingleThreadExecutor,CachedThreadPool)实质都是由ThreadPoolExecutor来实现的。

不同的构造方法共计可以设置6个参数:

corePoolSize:核心线程量,在默认的情况下核心线程是不会被销毁一直保持活跃状态的。可以通过allowCoreThreadTimeOut方法来设置。如何设置见下方代码。

maximumPoolSize:最大线程量,线程池允许创建的最大线程数量(包括核心线程和临时线程,临时限制在处于非活跃状态指定时间后会被销毁)。值得注意的是临时线程的创建时间。不是核心线程都处于运行状态来新任务时创建,而是核心线程都被占用,且队列被占满,此时来新任务才会创建临时线程。

keepAliveTime与unit:可允许非活跃时间(即临时线程(和设置可销毁后的核心线程)在处于非活跃状态多久之后会被线程池销毁)

workQueue:任务队列。一个阻塞队列,存放待执行任务。

threadFactory:线程工厂。

handler:拒绝策略。当线程池达到最大线程量且所以线程都被占用,任务队列已经满载的情况下或者线程池处于非Runing状态时新任务被提交时执行何种策略,可通过实现RejectedExecutionHandler接口自定义。jdk提供4种策略。线程池默认为AbortPolicy策略:    ①ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 拒绝执行异常,这里抛弃的是这个要加入线程池的任务    ②ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。这里抛弃的是这个要加入线程池的任务    ③ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)(注意:这里抛弃的不是这个要加入线程池的任务,而是线程池等待队列的首位,也就是线程池下一个要执行的任务。这里的实现是直接用e.getQueue().poll(),然后调用e.execute(r),r是要加入线程池的任务)    ④ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

可以通过submit和excute方法提交任务,区别在于submit可以获得返回(即可提交Callable)

ThreadPoolExecutor共有5种状态:
            RUNNING:接收新任务并处理队列中的任务
            SHUTDOWN:不接受新任务,但是处理队列任务
            STOP:不接受新任务,也不处理队列任务,并中断正在处理的任务。
            TIDYING:清理状态。所有的任务已经结束,当前工作线程为0.此时会调用terminated(),terminated()状态执行期间
            TERMINATED:terminated()方法执行完毕
                状态转换条件:
            RUNNING -> SHUTDOWN:调用shutDown()方法之后。或者是隐式的调用了protect级别的finalize()方法
            (RUNNING or SHUTDOWN) -> STOP:调用shutdownNow()方法之后
            SHUTDOWN -> TIDYING:当队列线程池都为空
            STOP -> TIDYING:线程池为空
            TIDYING -> TERMINATED:terminated()方法执行执行完毕


ThreadPoolExecutor各个方法(包括父类AbstractExecutorService中的方法)的简单使用见如下代码:

package ThreadPoolExecutor.ThreadPool;


import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;


public class ThreadPoolExecutorTest {


    public static void main(String[] args) {
        
        /**
         * 构造方法
         */
        //所有构造入参都可通过get方法获得,除阻塞队列外的其它几个可以通过set方法重置
        
        // 1参表示日常保持的线程数,
        // 2 表示任务来时间可以开启的最大线程数,值得注意的是临时线程的创建时间。不是核心线程都处于运行状态来新任务时创建,而是核心线程都被占用,且队列被占满,此时来新任务才会创建临时线程
        // 3.4参表示一个非核心线程空闲多久之后会被销毁
        // 5参:用来放等待任务的阻塞队列(BlockingQueue)
        // 
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 6, 100, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
//        threadPool.prestartCoreThread(); // 初始化一个核心线程;  
//        System.out.println(threadPool.getPoolSize()); // 1
//        threadPool.prestartCoreThread(); // 初始化一个核心线程;每次都是初始化一个
//        System.out.println(threadPool.getPoolSize()); // 2
//        threadPool.prestartCoreThread(); // 初始化一个核心线程;初始化到最大核心线程数时就不再初始化新的了
//        System.out.println(threadPool.getPoolSize()); // 2
//        System.out.println(threadPool.getCorePoolSize()); // 2
//        threadPool.prestartAllCoreThreads(); // 初始化所有核心线程
//        System.out.println(threadPool.getCorePoolSize()); // 2
        threadPool.setMaximumPoolSize(10);  // 设置最大线程数
        //  构造方法2   6参为线程创建工厂   默认为Executors.defaultThreadFactory()
        ThreadPoolExecutor threadPool2 = new ThreadPoolExecutor(3, 5, 100, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(),Executors.defaultThreadFactory());
        //  构造方法3   6参为拒绝策略,有4种,  一般是定长队列,当运行线程数达到最大线程数且线程缓存队列已满,此时依然有线程抛入线程池。就会执行拒绝策略.默认拒绝策略为AbortPolicy
        //ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 拒绝执行异常,这里抛弃的是这个要加入线程池的任务
        //ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。这里抛弃的是这个要加入线程池的任务
        //ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)(注意:这里抛弃的不是这个要加入线程池的任务,而是线程池等待队列的首位,也就是线程池下一个要执行的任务。这里的实现是直接用e.getQueue().poll(),然后调用e.execute(r),r是要加入线程池的任务)
        //ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
        ThreadPoolExecutor threadPool3 = new ThreadPoolExecutor(2, 5, 100, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), new ThreadPoolExecutor.AbortPolicy());
        // 构造方法4  其它构造其实都通过调用此构造实现
        ThreadPoolExecutor threadPool4 = new ThreadPoolExecutor(2, 5, 100, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());    
        ThreadPoolExecutor threadPool5 = 
                new ThreadPoolExecutor(2, 5, 100, TimeUnit.MILLISECONDS, 
                        new ArrayBlockingQueue<Runnable>(4));  // 这里设置成5不会拒绝执行抛出RejectedExecutionException
                                                               // 原因是总共10线程,前两个占据核心线程,再来5个进入缓存队列,剩下3个则会创建临时线程来执行任务,因此不会溢出
        /**
         * 线程提交                                                                                                                 
         */
        for (int i = 0; i < 10; i++) {
            final int j = i;
            Runnable r = new Runnable() {
                
                @Override
                public void run() {
                   System.out.println(j + "开始运行...");
                   try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                   System.out.println(j + "运行结束...");
                }
            };
            threadPool.execute(r);  // ThreadPoolExecutor的线程是一个个建立的,在线程池里面的线程数尚未到达最大线程数时间新任务到了会优先创建新的线程来执行而不是使用空闲线程执行
        }


        Callable<String> call = new Callable<String>() {
            
            @Override
            public String call() throws Exception {
                System.out.println("Callable");
                Thread.sleep(1000);
                System.out.println("Callable Sleep End");
                return "This is Callable";
            }
        };
//        Future<String> f = threadPool.submit(call);// 可以提交Callable获得执行结果,这里就体现出submit和excute的区别了,submit可以获得返回
//        try {
//            Thread.sleep(500);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
//        f.cancel(true);    // 取消Callable任务的执行,参数为true时如果任务已经在执行则会尝试中断这个任务,参数为false时如果任务已经在执行了则会允许它执行结束。取消之后再调用get方法会抛java.util.concurrent.CancellationException异常。
//                            // 值得注意的是,即使参数设为false,且取消指令在Callable执行过程中调用。那么任务是会执行完,但是即使这样,调用get方法依然会抛出异常.
//                            // 如这个地方,在Callable执行时取消,设为true时不会打印Callable Sleep End,false时会打印。但是之后的get方法都会报异常。
//        try {
//            System.out.println("the return of callable:" + f.get());   // 这里调用get会使主线程停止。future的主线程停止是在get这里而不是submit那里
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        } catch (ExecutionException e) {
//            e.printStackTrace();
//        }
        
        
        Runnable rno = new Runnable() {
            
            @Override
            public void run() {
                System.out.println("not need");
            }
        };
        threadPool.execute(rno);  // 添加一个无需执行的任务
        threadPool.remove(rno); // 从等待队列中移除任务
        
        System.out.println("getPoolSize -- :" + threadPool.getPoolSize());  // 当前线程池中有多少线程
        System.out.println("getActiveCount -- :" + threadPool.getActiveCount());  // 线程池中活动线程数量
        System.out.println("getLargestPoolSize -- :" + threadPool.getLargestPoolSize()); // 线程池中曾有过的最大线程量
        System.out.println("getTaskCount -- :" + threadPool.getTaskCount()); //  包括正在执行的在内预计要执行的任务量,因为任务和线程的状态可能在计算过程中动态变化,返回的值只是一个近似值。
        System.out.println("getCompletedTaskCount -- :" + threadPool.getCompletedTaskCount()); // 线程池已完成多少任务
        System.out.println("allowCoreThreadTimeOut -- :" + threadPool.allowsCoreThreadTimeOut()); // 超时空闲销毁时间是否针对核心线程(默认为false,即核心线程会一直保持)
        threadPool.allowCoreThreadTimeOut(true);// 设置为true时不仅临时线程空闲时间超过keepAliveTime会被销毁,核心线程也会超时空闲销毁
        threadPool.purge(); //将队列中已经取消的fature移除(fature可以取消Callable任务的执行)。这个方法不影响任何功能,只是相当于一个提前的垃圾处理操作(不调用这个方法线程池也会自动处理),这个方法可能因为其它线程影响导致失败。
        
//        threadPool.shutdown(); //  进入shutDown状态,不再接收新的任务
//        System.out.println("isTerminating()--:" + threadPool.isTerminating()); // SHUTDOWN,STOP,TIDYING三种状态都返回true,这里会返回true。
//        try {
//            System.out.println("awaitTermination" + threadPool.awaitTermination(1, TimeUnit.SECONDS));  // awaitTermination方法只是一个监听方法,他不参与具体的线程池运行。他只是在调用这个方法之后等待指定时间
//                                                                            //然后返回线程池是否已经关闭。true:已经关闭。 false:未关闭
//        } catch (InterruptedException e1) {
//            System.out.println("awaitTermination err");
//            e1.printStackTrace();
//        }


        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        threadPool.shutdown(); //  进入shutDown状态,不再接收新的任务。他会等待线程池中所有的任务都执行完毕
//      threadPool.shutdownNow(); // 立即关闭线程池,中断当前线程被清空缓存队列。
        Runnable rn = new Runnable() {
            
            @Override
            public void run() {
                System.out.println("new runnable");
            }
        };
//        threadPool.execute(rn);  // 线程池已得到关闭指令,继续添加则根据拒绝策略执行响应操作
        
        System.out.println("isTerminating()--:" + threadPool.isTerminating()); // SHUTDOWN,STOP,TIDYING三种状态都返回true
        System.out.println("isTerminated()--:" + threadPool.isTerminated()); // TERMINATED 状态
        System.out.println("isShutdown()--:" + threadPool.isShutdown()); // 注意不要被方法的名称所迷糊,并不是只有处于SHUTDOWN状态才返回TRUE,只要不是RUNNING状态都返回true
       /* 
        ThreadPoolExecutor共有5种状态:
            RUNNING:接收新任务并处理队列中的任务
            SHUTDOWN:不接受新任务,但是处理队列任务
            STOP:不接受新任务,也不处理队列任务,并中断正在处理的任务。
            TIDYING:清理状态。所有的任务已经结束,当前工作线程为0.此时会调用terminated(),terminated()状态执行期间
            TERMINATED:terminated()方法执行完毕
                状态转换条件:
            RUNNING -> SHUTDOWN:调用shutDown()方法之后。或者是隐式的调用了protect级别的finalize()方法
            (RUNNING or SHUTDOWN) -> STOP:调用shutdownNow()方法之后
            SHUTDOWN -> TIDYING:当队列线程池都为空
            STOP -> TIDYING:线程池为空
            TIDYING -> TERMINATED:terminated()方法执行执行完毕
        */
        
        
        /**AbstractExecutorService方法*/
        System.out.println("start test AbstractExecutorService");
        List<Callable<String>> callList = new ArrayList<>();
        final AtomicInteger ai = new AtomicInteger();
        for (int i = 0; i < 10; i++) {
            final int j = i;
            Callable<String> callTemp = new Callable<String>() {
                @Override
                public String call() throws Exception {
                    System.out.println("Callable" + j);
                    Thread.sleep(1000);
                    System.out.println("Callable Sleep End" + j);
                    return "This is Callable" + j;
                }
            };
            callList.add(callTemp);
        }
        try {
//            for (int i = 0; i < 11; i++) {
//                System.out.println(threadPool2.invokeAny(callList));    // 执行集合中的任意一条。一旦有一个任务执行完毕就会返回结果并取消其它线程。(个人认为这是一件很危险的事情:如果有A,B两个线程在这个集合,空闲线程大于2导致A,B共同执行,那么如果A执行完毕返回,B执行一半,那么B被取消,但是取消之后B前半部分造成的影响是无法回滚的!!!这需要注意)
//                Thread.sleep(1000);
//            }
            List<Future<String>> result = threadPool2.invokeAll(callList);// 把线程投入线程池并返回Future集合。List结果的顺序和callList的顺序是一样的
            for (Future<String> future : result) {
                System.out.println(future.get());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        
    }
}


参考:Java并发编程:线程池的使用

发布了29 篇原创文章 · 获赞 11 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/yue_hu/article/details/79192109