美文网首页
5-Executor

5-Executor

作者: 鹏程1995 | 来源:发表于2020-02-11 10:57 被阅读0次

    概述

    引入

    我们之前介绍了

    • Runnable,Callable类,用来将我们的逻辑代码进行封装
    • Future类,在上面的基础上增加了任务进度的控制及检测
    • FutureTask类,用来兼容我们封装的Callable/Runnable接口差异

    我们之前讲的都是对任务的封装和执行效果的检测、控制,但是还有重要的一环没有做——创建子线程执行任务。

    我们之前都是在主线程中自行创建线程,执行子任务。这种方法简单粗暴,但是有以下问题:

    1. 重复的、不涉及业务逻辑的代码不便于编程
    2. 频繁创建线程,在性能上有很大的开销
    3. 在极端调用情况下,无法合理利用机器资源【比如同时创建200个线程执行任务,电脑卡死,不一定比创建20个线程复用执行快】

    我们今天了解的就是如何管理子线程的创建的接口Executor。此接口最开始设计的目的是将任务提交与任务执行机制分离,从而更方便、科学的管理子任务的执行。

    摘要

    本文介绍了Executor接口的设计理念,并自行设计了一个Executor的实现类以减少大量创建子任务的编码、执行成本。

    类介绍

    类定位

    设计此类用来将任务提交和任务执行机制分离,即将子线程的来路和子线程的管理从程序员手中取出来,专门实现一套完善、复杂的程序猿好用,资源利用效率还高。

    注意

    源码解读

    /**
     * 稍后执行提交的任务,该任务可能在以下几种线程中执行:
     * 1. 新创建的线程
     * 2. 线程池中的线程
     * 3. 调用此方法的主线程
     *
     * @param command 任务逻辑代码封装成的接口实现类
     * @throws RejectedExecutionException 如果 Executor 拒绝执行此任务抛出的异常
     * @throws NullPointerException 如果入参为空抛出的异常
     */
    void execute(Runnable command);
    

    使用示例

    示例

    package com.gateway.concurrent.pool;
    
    import java.io.IOException;
    import java.util.concurrent.BlockingQueue;
    import java.util.concurrent.Executor;
    import java.util.concurrent.LinkedBlockingDeque;
    
    /**
     * @author lipengcheng3 Created date 2019-02-22 18:18
     */
    public class MyThreadExecutor implements Executor {
    
        private final Thread[] threads = new Thread[5];
    
        private final BlockingQueue<Runnable> tasks = new LinkedBlockingDeque<>();
    
        public MyThreadExecutor() {
            for (int i = 0; i < 5; i++) {
                threads[i] = new Thread(new MyRunnable());
                threads[i].setDaemon(true);
                threads[i].start();
    
            }
        }
    
        @Override
        public void execute(Runnable command) {
            try {
                tasks.put(command);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        class MyRunnable implements Runnable {
    
            private volatile boolean flag = true;
    
            @Override
            public void run() {
                while (flag) {
                    try {
                        Runnable runnable = tasks.take();
                        runnable.run();
                    } catch (Exception e) {
                        e.printStackTrace();
                        flag = false;
                    }
                }
            }
        }
    
    
        public static void main(String args[]) throws IOException, InterruptedException {
            MyThreadExecutor myThreadExecutor = new MyThreadExecutor();
            for (int i = 0; i < 40; i++) {
                myThreadExecutor.execute(new TestCalss("任务" + i));
            }
            Thread.sleep(1000);
    
        }
    
        static class TestCalss implements Runnable {
    
            private String name;
    
            public TestCalss(String name) {
                this.name = name;
            }
    
            @Override
            public void run() {
                Long sleepTime = (long) (Math.random() * 1000);
                try {
                    Thread.sleep(sleepTime);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(name + " 完成,用时 " + sleepTime + " 毫秒");
            }
        }
    
    }
    
    

    核心逻辑介绍

    思路很简单我们分三个块介绍:

    1. MyThreadExecutor的运行机制

    我们使用一个固定长度为5的数组,创建了5个线程,运行我们定义好的MyRunnable的逻辑:即不停的从一个阻塞队列中取任务执行。

    向外暴露的execute方法,用于将任务不停的放入任务的阻塞队列中。

    1. main的运行机制

    创建MyThreadExecutor实例后不停的往里放任务,放完后等待1000ms后退出程序。

    使用思路

    其实Executor的实现思路很简单,我们要着重处理好以下几个问题:

    1. 任务的添加和获取的线程安全性

    2. 一个Thread在创建时。他执行的内容就已经确定了,我们应如何将Runnable任务传给已经创建好的Thread并执行以实现线程复用的问题

    3. 如何处理好线程的回收问题

      这里我就遇到了这个问题,虽然我们在代码中也没有做回收操作。一开始在运行程序时发现执行结束后程序不会退出。想了一下下秒懂,是应为那5个子线程还在跑。于是把那几个线程从用户线程切换为守护线程,这样在主线程也是唯一的用户线程等待1000ms退出后子线程随着jvm的停止自动被回收。

    问题

    在这里好好想想,怎么搞能写出伸缩性更好、更灵活易用的Executor工具。

    其实我上面写的实现已经有点像线程池了,只不过我都是写死并封装的,对用户透明。

    扩展

    参考文献

    相关文章

      网友评论

          本文标题:5-Executor

          本文链接:https://www.haomeiwen.com/subject/cjgtfhtx.html