Java学习笔记5-线程池

Java学习笔记5-线程池

线程池原理 - 我的理解就是个送快递的网点

  1. 线程池管理器:用于创建并管理线程池,包括创建线程池,销毁线程池,添加新任务;- 快递网点管理者,可以招聘和辞退快递小哥,收件分件等
  2. 工作线程:线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;- 送快递的小哥
  3. 任务接口:每个任务必须实现的接口,以供工作线程调度任务的执行,他主要规定了任务的入口,任务执行完成后的收尾工作,任务的执行状态等;- 快递包裹的规格,快递单及配送信息等,没地址的快递小哥可没法送
  4. 任务队列:用于存放没有处理的任务。提供一种缓冲机制。- 存放快递包裹的仓库

线程池API

  1. Executor(接口):最上层的接口,定义了执行任务的方法execute

  2. ExecutorService(接口):继承Executor接口,扩展了Callable、Future、关闭方法

  3. ScheduledExecutorService(接口):继承ExecutorService接口,增加了定时任务相关方法

  4. ThreadPoolExecutor(实现类):基础、标准的线程池实现,推荐使用,创建线程达到核心线程数 -> 再将任务队列放满 -> 可创建线程达到最大线程数 -> 执行拒绝策略

    public ThreadPoolExecutor(int corePoolSize, // 核心线程数量
                              int maximumPoolSize, // 最大线程数量
                              long keepAliveTime, // 超出核心线程数量的线程空闲存活时间
                              TimeUnit unit, // 存活时间单位
                              BlockingQueue<Runnable> workQueue, // 等待任务队列
                              ThreadFactory threadFactory, // 线程创建工厂,可选
                              RejectedExecutionHandler handler // 指定拒绝策略的,可选
    ) {
        // ...
    }
    

    ThreadPoolExecutor测试代码

    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.RejectedExecutionHandler;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @Author: Wenx
     * @Description:
     * @Date: Created in 2019/11/11 17:14
     * @Modified By:
     */
    public class DemoTest {
        public static void main(String[] args) throws InterruptedException {
            // 创建一个 核心线程数量为2,最大线程数量为5,等待队列为3 的线程池,相当于最大容纳8个任务
            // 默认的策略是抛出RejectedExecutionException异常,java.util.concurrent.ThreadPoolExecutor.AbortPolicy
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                    2, 5, 5, TimeUnit.SECONDS,
                    new LinkedBlockingQueue<Runnable>(3),
                    new RejectedExecutionHandler() {
                        @Override
                        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                            System.err.println("快递网点:快递小哥不够仓库已满 / 停止业务 -> 拒收快递包裹");
                        }
                    });
    
            // 测试: 提交 10个 执行时间需要3秒的任务,看超过2个任务时的对应处理情况
            int num = 10;
            for (int i = 0; i < num; i++) {
                int n = i + 1;
                System.out.println("快递包裹" + n + ":到达快递网点");
                threadPoolExecutor.submit(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            System.out.println("------------------------------快递小哥正在送 快递包裹" + n);
                            Thread.sleep(3000L);
                            System.out.println("------------------------------快递包裹" + n + " 已送达");
                        } catch (InterruptedException e) {
                            System.err.println("------------------------------快递小哥翻车了:" + e.getMessage());
                        }
                    }
                });
                System.out.println("派件快递小哥人数为:" + threadPoolExecutor.getPoolSize());
                System.out.println("网点仓库包裹数量为:" + threadPoolExecutor.getQueue().size());
            }
    
            // 8秒后快递包裹应该都配送完了(具体延迟时间可以调整至都配送完,还有5人=2核心+3临时)
            Thread.sleep(8000L);
            System.out.println("快递网点:快递包裹应该差不多配送完了");
            System.out.println("派件快递小哥人数为:" + threadPoolExecutor.getPoolSize());
            System.out.println("网点仓库包裹数量为:" + threadPoolExecutor.getQueue().size());
    
            // 没活干5秒的临时快递小哥要被辞退了(空闲5秒就要被辞退,就剩核心2人)
            Thread.sleep(5000L);
            System.out.println("快递网点:没活干5秒的临时快递小哥要被辞退");
            System.out.println("派件快递小哥人数为:" + threadPoolExecutor.getPoolSize());
            System.out.println("网点仓库包裹数量为:" + threadPoolExecutor.getQueue().size());
    
            threadPoolExecutor.shutdown(); // 快递网点停止业务,但现有快递包裹还会配送
            //threadPoolExecutor.shutdownNow(); // 快递网点停止业务,现有快递包裹也不会配送
            System.out.println("快递网点:停止业务");
            Thread.sleep(1000L);
            System.out.println("派件快递小哥人数为:" + threadPoolExecutor.getPoolSize());
            System.out.println("网点仓库包裹数量为:" + threadPoolExecutor.getQueue().size());
            // 再次提交提示失败
            System.out.println("快递包裹" + (num + 1) + ":到达快递网点");
            threadPoolExecutor.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("又来一个快递包裹");
                }
            });
    
            // 结果分析
            // 1、 2个任务(快递包裹)直接分配线程(快递小哥)开始执行(配送)
            // 2、 3个任务(快递包裹)进入等待队列(快递网点仓库)
            // 3、 队列(仓库)不够用,临时加开3个线程(快递小哥)来执行任务(5秒没活干就辞退)
            // 4、 队列(仓库)和线程池(快递小哥)都满了,剩下2个任务(快递包裹),没资源了,被拒绝执行(拒收)。
            // 5、 等待现有任务执行结束(配送完),应当还有5个任务(5人=2核心+3临时)
            // 6、 5秒后,如果无任务可执行(没活干),销毁临时创建的3个线程(辞退临时快递小哥)
            // 7、 调用shutdown(停止业务),不接收新的任务(快递包裹)
            // 8、 在线程池关闭(停止业务)后追加的任务(快递包裹),无法再提交,会被拒绝执行(拒收)
        }
    }
    
  5. ScheduledThreadPoolExecutor(实现类):定时任务线程池实现,继承了ThreadPoolExecutor,实现了ScheduledExecutorService中定时任务相关的方法

    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, // 核心线程数量
              Integer.MAX_VALUE, // 最大线程数量
              0, // 超出核心线程数量的线程空闲存活时间
              NANOSECONDS, // 存活时间单位
              new DelayedWorkQueue() // 等待任务队列-延时队列,没有到达时间取不出来
        );
    }
    

    ScheduledThreadPoolExecutor测试代码

    import java.text.SimpleDateFormat;
    import java.util.concurrent.ScheduledThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @Author: Wenx
     * @Description:
     * @Date: Created in 2019/11/11 17:14
     * @Modified By:
     */
    public class DemoTest {
        public static void main(String[] args) throws InterruptedException {
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
            ScheduledThreadPoolExecutor threadPoolExecutor = new ScheduledThreadPoolExecutor(5);
    
            // 定时3秒后执行一次任务
            threadPoolExecutor.schedule(new Runnable() {
                @Override
                public void run() {
                    System.out.println("定时任务1 开始执行,当前时间为:" + dateFormat.format(System.currentTimeMillis()));
                }
            }, 3000, TimeUnit.MILLISECONDS);
            System.out.println("定时任务1 提交成功,当前时间为:" + dateFormat.format(System.currentTimeMillis()));
            System.out.println("当前线程池中线程数量:" + threadPoolExecutor.getPoolSize());
    
            // 周期性执行任务
            // scheduleAtFixedRate:2秒后执行第一次任务,之后每间隔1秒执行一次(如果上次执行还未结束,则等待结束后立刻执行)
            threadPoolExecutor.scheduleAtFixedRate(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(3000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("定时任务2 开始执行,当前时间为:" + dateFormat.format(System.currentTimeMillis()));
                }
            }, 2000, 1000, TimeUnit.MILLISECONDS);
            System.out.println("定时任务2 提交成功,当前时间为:" + dateFormat.format(System.currentTimeMillis()));
            System.out.println("当前线程池中线程数量:" + threadPoolExecutor.getPoolSize());
    
            // scheduleWithFixedDelay:2秒后执行第一次任务,之后每间隔1秒执行一次(如果上次执行还未结束,则等待结束后,再等待1秒执行)
            threadPoolExecutor.scheduleWithFixedDelay(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(3000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("定时任务3 开始执行,当前时间为:" + dateFormat.format(System.currentTimeMillis()));
                }
            }, 2000, 1000, TimeUnit.MILLISECONDS);
            System.out.println("定时任务3 提交成功,当前时间为:" + dateFormat.format(System.currentTimeMillis()));
            System.out.println("当前线程池中线程数量:" + threadPoolExecutor.getPoolSize());
        }
    }
    
  6. Executors(工具类):创建线程池工厂类,不推荐使用,因为创建的都是无界队列线程池,线程处理不完的话,队列有多少收多少,推荐直接使用ThreadPoolExecutor配置有界队列

    newFixedThreadPool(int nThreads):创建一个固定大小的线程池

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

    newCachedThreadPool():创建一个缓冲线程池,可以动态调整线程池大小

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                60L, TimeUnit.SECONDS,
                new SynchronousQueue<Runnable>());
    }
    

    newSingleThreadExecutor():创建一个单一线程池

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

    newScheduledThreadPool(int corePoolSize):创建一个定时执行任务的线程池

    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值