06:线程池

1:线程池原理-基本概念:
    1:线程池管理器:用户管理线程池。包括创建线程池、销毁线程池,添加新任务等。
    2:工作线程:工作线程就是线程池中实际工作的线程。没有任务时:处于等待状态,有任务时:可以循环的执行任务。
    3:任务接口:每个任务都需要实现的接口。规范了任务的输入、输出等。
    4:任务队列:任务太多时,超过了线程池处理能力。将待处理的任务放到等待队列中。
2:线程池接口和实现类:
    1:接口:Executor:最上层的接口:定义了执行任务的方法:executor()
    2:接口:ExecutorService:继承了Executor接口,扩展了Callable,Future,关闭方法。
    3:接口:ScheduledExecutorService:继承了ExecutorService,怎加了定时任务相关的方法。
    4:实现类:ThreadPoolExecutor:标准的线程池实现。但是比较基础。
    5:实现类:ScheduledThreadPoolExecutor:继承了ThreadPoolExecutor,实现了ScheduledExecutorService。也就是说:在标准的线程池类基础上怎加了定时任务。

 

3:ThreadPoolExecutorc测试:

/**
 *  1:核心线程数量5,最大数量10,无界队列,超出核心线程数量的线程存活时间5秒。
 */                                                       // 没有达到线程池最大线程数量10,而是进入了等待队列。
private static void threadPoolExecutorTest1() throws Exception{                    当前线程池线程数量为:5
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(                  当前线程池等待队列的长度:5     
            5, // 核心线程数量                                       线程结束:7
            10, // 最大线程数量                                       ...
            5, // 超出核心线程数量的线程的存活时间                            线程结束:14
            TimeUnit.SECONDS, // 单位                                   当前线程池线程数量为:5
            new LinkedBlockingQueue<Runnable>()); // 线程等待队列                   当前线程池等待队列的长度:0
    testCommon(threadPoolExecutor);                                    // 因为没有把队列占满,只有等待队列满了才会超出核心线程数量
}
/**
 *  1:等待队列长度为3,2: 指定拒绝策略。
 */ 
private static void threadPoolExecutorTest2() throws Exception{
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
            5, 10, 5, TimeUnit.SECONDS,
            new LinkedBlockingQueue<Runnable>(3),                             等待队列调小为3               
            new RejectedExecutionHandler() {                                        // 如果(等待队列满了 && 最大线程数量也达到了):执行拒接策略
                @Override
                public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                    System.out.println("任务被拒绝执行。");
                }
            }
    );
    testCommon(threadPoolExecutor);
}
/**
 * 1:核心线程数量 = 最大线程数 , 队列无界
 */
private static void threadPoolExecutorTest3()throws Exception{
    // 和线程池工具类的 Executors.newFixedThreadPool(int nThreads)实现一样。        // 和Executors.newFixedThreadPool(int nThreads)实现一样
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
            5, 5, 5, TimeUnit.SECONDS,
            new LinkedBlockingQueue<Runnable>()
    );
    testCommon(threadPoolExecutor);
}
/**
 * SynchronousQueue,实际上不是一个真正的队列,因为他不会为队列中的元素维护存储空间。他维护的是一组线程。
 * 这种线程池起初没有线程,最后执行完成后也没有线程。
 * 60秒后无用的线程就会销毁
 */
private static void threadPoolExecutorTest4() throws Exception{                // 和Executors.newCachedThreadPool() 实现一样
    // 和 Executors.newCachedThreadPool() 方法一样                       // 当无法预估需要多少线程,但是最大线程数量自己设定。不然太大了。
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(0,Integer.MAX_VALUE, 60L,TimeUnit.SECONDS,   // 如果运行过程中有线程,就复用。没有就加开。     
            new SynchronousQueue<Runnable>());                            // 不工作的线程,60秒后自动销毁。
}                                                     //  注意控制最大线程数量。
/**
 * 定时执行 : 3秒后执行,一次性执行,到时间就执行。
 *           核心线程数:5 , 最大线程数为:Integer.MAX_VALUE
 *           DelayedWorkQueue 为延时队列,存放进去一定时间后才可以取出来。
 * @throws Exception
 */
private static void threadPoolExecutorTest5() throws Exception{                // 定时任务,延时执行一次。
    // Executors.newScheduledThreadPool()
    ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(5);    // 如果你的任务需要很多线程,就调多一点。
    scheduledThreadPoolExecutor.schedule(new Runnable() {
        @Override
        public void run() {
            System.out.println("任务执行");
        }
    },
    3000,
    TimeUnit.MICROSECONDS);        // 3秒之后执行
}
/**
 * 定时任务,周期性执行。(固定频率,固定延迟)
 * 固定线程数:5 核心线程数5 最大线程数Integer.MAX_VALUE 延时队列,超出核心线程数量的线程存活0秒。
 * 例如:需要定时查询数据库的任务。
 */
private static void threadPoolExecutorTest6() throws Exception{
    // 周期性执行任务有两种调度方式。
    ScheduledThreadPoolExecutor threadPoolExecutor = new ScheduledThreadPoolExecutor(5);
    // 第一种:固定频率:提交后:2秒后开始第一次执行,之后每间隔一秒,固定执行一次。(如果发现上次任务未完成,等待完毕后立即执行下一个任务)
    threadPoolExecutor.scheduleAtFixedRate(new Runnable() {
        @Override
        public void run() {
                System.out.println("如果任务执行时间超出了周期时间,下一个任务直接开始");
        }
    },2000 , 1000 ,TimeUnit.MICROSECONDS);
    // 第二种:固定延迟:提交后:两秒后开始第一次执行,之后每间隔一秒,固定执行一次。(如果发现上次任务未完毕,等上一次完毕后,再等待一秒。然后执行下一个任务)
    threadPoolExecutor.scheduleWithFixedDelay(new Runnable() {
        @Override
        public void run() {
            System.out.println("如果任务执行时间超出了周期时间,下一个任务重新计算执行延时后再执行");
        }
    },2000,1000,TimeUnit.MICROSECONDS);
}
/**
 * 测试:提交15个执行时间为3秒的任务。并输出线程状态。
 * @param threadPoolExecutor 显示池
 */
private static void testCommon(ThreadPoolExecutor threadPoolExecutor)throws Exception{
    for (int i = 0; i < 15; i++) {
        int n = i;
        threadPoolExecutor.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("开始时间:"+n);
                    Thread.sleep(3000L);
                    System.out.println("线程结束:"+n);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        System.out.println("任务提交成功"+i);
    }
    // 等待5秒后看线程池状态
    Thread.sleep(5000L);
    System.out.println("当前线程池线程数量为:"+threadPoolExecutor.getPoolSize());
    System.out.println("当前线程池等待队列的长度:"+threadPoolExecutor.getQueue().size());
    // 等待15秒,查看线程池状态(理论上,超出核心线程数量的线程会被销毁)
    Thread.sleep(10000L);
    System.out.println("当前线程池线程数量为:"+threadPoolExecutor.getPoolSize());
    System.out.println("当前线程池等待队列的长度:"+threadPoolExecutor.getQueue().size());
}

 

 4:可以用线程池工具类快速创建线程池:

5:如何合适的定义线程池的线程数量:
    1:计算型任务(计算数据任务):线程的数量是CPU熟练的 1~2倍即可。
    2:IO型任务(网络操作,数据库操作,文件操作):根据具体的IO阻塞时长考量。就是说阻塞的时候开多线程执行。
    3:考虑设置最小的线程数量和最大线程数量。自动在这个区间怎加线程数量。
    4:窍门:通过监控CPU的使用率。达到80%就说明cpu利用起来了。

posted on 2020-03-30 20:50  笑明子  阅读(132)  评论(0编辑  收藏  举报

导航