6.线程池应用

2020-03-03  本文已影响0人  强某某

线程是不是越多越好?

  1. 线程在java中是一个对象,更是操作系统的资源,线程创建,销毁需要时间。如果创建时间+销毁时间》执行任务时间就很不合算。
  2. java对象占用堆内存,操作系统线程占用系统内存,根据jvm规范,一个线程默认最大栈大小1M,这个栈空间时需要从系统内存中分配的。线程过多,会消耗很多的内存。
  3. 操作系统需要频繁切换线程上下文(大家都想被运行),影响性能。

线程池的推出,就是为了方便的控制线程数量

线程池原理

  1. 线程池管理器:用于创建并管理线程池,包括创建线程池,销毁线程池,添加新任务;
  2. 工作线程:线程池中线程,在没有任务时处于等待状态,可以循环的执行任务
  3. 任务接口:每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完之后的收尾工作,任务的执行状态等
  4. 任务队列:用于存放没有处理的任务,提供一种缓冲机制


    1.png

线程池API-接口定义和实现类

2.png
  1. ExecutorService


    3.png
  2. ScheduledExecutorService


    4.png

示例代码:

private void threadPoolExecutorTest1() throws Exception {
        //线程池信息:核心线程数量5,最大数据10,无界队列,超出核心线程数量的线程存活时间:5秒,指定拒绝策略
        /**
         * 无界队列:对任务队列不限制,可以无限添加
         * 线程池当前核心线程数据5,如果第六个线程持续五秒无任务则第六个线程关闭
         */
        ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(5,10,5, TimeUnit.SECONDS,new LinkedBlockingQueue<>());
        testCommon(threadPoolExecutor);
    }

    public void testCommon(ThreadPoolExecutor threadPoolExecutor) throws Exception{
        for (int i = 0; i < 15; i++) {
            int n=i;
            threadPoolExecutor.submit(()->{
                try {
                    System.out.println("开始执行:"+n);
                    Thread.sleep(3000L);
                    System.out.println("执行结束:"+n);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            System.out.println("任务提交成功:"+i);
        }
        Thread.sleep(500L);
        System.out.println("当前线程池线程数量为:"+threadPoolExecutor.getPoolSize());
        System.out.println("当前线程池等待的数量为:"+threadPoolExecutor.getQueue().size());
        //等待十五秒,理论上,超出核心线程数量的线程都会被取消
        Thread.sleep(1500L);
        System.out.println("当前线程池线程数量为:"+threadPoolExecutor.getPoolSize());
        System.out.println("当前线程池等待的数量为:"+threadPoolExecutor.getQueue().size());
    }

在执行日志可以看出,理论上任务数多于核心线程,应该启动新线程直到最大线程数,但是实际上并没有。原因如下:


5.png

所以,核心就在于,队列数,上面的代码是无界队列,所以永远队列不会满也就不会启动超过核心线程数的线程

示例代码二

private void threadPoolExecutorTest1() throws Exception {
        //线程池信息:核心线程数量5,最大数据10,等待队列最大是3,也就是说最大容纳13个任务,超出核心线程数量的线程存活时间:5秒,指定拒绝策略
        //默认的策略是抛出RejectedExecutionException异常,java.util.concurrent.ThreadPoolExecutor.AbortPolicy
        //此处已经做了RejectedExecutionHandler处理,则会打印内部的输出
        ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(3), new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                System.out.println("有任务被拒绝执行了");
            }
        });
        testCommon(threadPoolExecutor);
    }
  1. Executors工具类
    可以自己实例化线程池,例如上面的代码,也可以用Executors创建的线程池的工厂类,常用方法如下:

单次定时任务代码示例

//定时执行线程池信息:3秒后执行,一次性任务,到点就执行
    //核心线程数量5,最大数量Integer.MAX_VALUE. 其本质就是延迟队列:DelayedWorkQueue,超出核心线程数量线程存活0秒
     private void threadPoolExecutorTest2() throws Exception{
         ScheduledThreadPoolExecutor threadPoolExecutor=new ScheduledThreadPoolExecutor(5);
         threadPoolExecutor.schedule(()->{
             System.out.println("任务呗执行:"+System.currentTimeMillis());
         },3000,TimeUnit.MILLISECONDS);
     }

循环执行任务代码示例

private void threadPoolExecutorTest2() throws Exception{
         ScheduledThreadPoolExecutor threadPoolExecutor=new ScheduledThreadPoolExecutor(5);
         //这个1秒,如果上次执行时间超过一秒,则该次任务立刻开始
         threadPoolExecutor.scheduleAtFixedRate(()->{
             try {
                 Thread.sleep(3000L);
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
             System.out.println("任务1被执行:"+System.currentTimeMillis());
             //2秒后第一次执行,间隔1秒
         },2000,1000,TimeUnit.MILLISECONDS);


         //这个1秒值得上上一次任务结束之后一秒
         threadPoolExecutor.scheduleWithFixedDelay(()->{
             try {
                 Thread.sleep(3000L);
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
             System.out.println("任务2被执行:"+System.currentTimeMillis());
         },2000,1000,TimeUnit.MILLISECONDS);
     }

shutdown优雅关闭代码示例:等待正在运行完成后关闭,队列中也会执行,但是不接收新任务

 private void threadPoolExecutorTest1() throws Exception {
        ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(3), new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                System.out.println("有任务被拒绝执行了");
            }
        });
        testCommon(threadPoolExecutor);
    }

    public void testCommon(ThreadPoolExecutor threadPoolExecutor) throws Exception{
        for (int i = 0; i < 15; i++) {
            int n=i;
            threadPoolExecutor.submit(()->{
                try {
                    System.out.println("开始执行:"+n);
                    Thread.sleep(3000L);
                    System.out.println("执行结束:"+n);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            System.out.println("任务提交成功:"+i);
        }
        Thread.sleep(1000L);
        threadPoolExecutor.shutdown();
        threadPoolExecutor.submit(()->{
            System.out.println("追加一个任务"); 
        });
    }
    //结果分析:
    //1. 10个任务被执行,3个任务进入等待队列,2个任务被拒绝执行
    //2. 调用shutdown后,不接收新的任务,等待13个任务执行结束,所以“追加一个新任务”不会打印
    //3. 追加的任务在线程池关闭后,无法再提交,会被拒绝执行,打印"有任务被拒绝执行了"

强制关闭代码示例:强制终止

private void threadPoolExecutorTest1() throws Exception {
        ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(3), new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                System.out.println("有任务被拒绝执行了");
            }
        });
        testCommon(threadPoolExecutor);
    }

    public void testCommon(ThreadPoolExecutor threadPoolExecutor) throws Exception{
        for (int i = 0; i < 15; i++) {
            int n=i;
            threadPoolExecutor.submit(()->{
                try {
                    System.out.println("开始执行:"+n);
                    Thread.sleep(3000L);
                    System.out.println("执行结束:"+n);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    System.out.println("异常");
                }
            });
            System.out.println("任务提交成功:"+i);
        }
        Thread.sleep(1000L);
        List<Runnable> runnables = threadPoolExecutor.shutdownNow();
        threadPoolExecutor.submit(()->{
            System.out.println("追加一个任务");
        });
        System.out.println("未结束的任务有"+runnables.size());
    }
    /**
    结果分析
    1. 1个任务被执行,3个任务进入等待队列,2个任务被拒绝执行
    2. 调用shutdownNow后,队列中的3个线程不再执行,10个正在执行的线程被强制终止(打印异常)
    */
  1. 如果确定合适数量的线程?
    计算型任务:cpu数量的1-2倍
    IO型任务:相对比计算型任务,需多一些线程,要根据具体IO阻塞时长进行考量决定。
    如tomcat中默认的最大线程数为:200.
    也可以考虑根据需要再一个最大数量和最小数量之间自定变化
上一篇 下一篇

猜你喜欢

热点阅读