Android线程池封装库

1次阅读

共计 17133 个字符,预计需要花费 43 分钟才能阅读完成。

目录介绍

  • 1. 遇到的问题和需求
  • 1.1 遇到的问题有哪些
  • 1.2 遇到的需求
  • 1.3 多线程通过实现 Runnable 弊端
  • 1.4 为什么要用线程池
  • 2. 封装库具有的功能
  • 2.1 常用的功能
  • 3. 封装库的具体使用
  • 3.1 一键集成
  • 3.2 在 application 中初始化库
  • 3.3 最简单的 runnable 线程调用方式
  • 3.4 最简单的异步回调
  • 4. 线程池封装思路介绍
  • 4.1 自定义 Runnable 和 Callable 类
  • 4.2 添加回调接口 Callback
  • 4.3 创建线程池配置文件
  • 4.4 创建 java 和 android 平台消息器
  • 4.5 创建 PoolThread 继承 Executor
  • 4.6 使用 builder 模式获取线程池对象
  • 4.7 灵活创建线程池[重点]
  • 4.8 启动线程池中的任务
  • 4.9 如何关闭线程池操作
  • 5. 其他介绍
  • 5.1 参考的开源案例
  • 5.2 参考的博客

好消息

  • 博客笔记大汇总【16 年 3 月到至今】,包括 Java 基础及深入知识点,Android 技术博客,Python 学习笔记等等,还包括平时开发中遇到的 bug 汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是 markdown 格式的!同时也开源了生活博客,从 12 年起,积累共计 N 篇[近 100 万字,陆续搬到网上],转载请注明出处,谢谢!
  • 链接地址:https://github.com/yangchong2…
  • 如果觉得好,可以 star 一下,谢谢!当然也欢迎提出建议,万事起于忽微,量变引起质变!

0. 前言介绍

  • 轻量级线程池封装库,支持线程执行过程中状态回调监测(包含成功,失败,异常等多种状态);支持创建异步任务,并且可以设置线程的名称,延迟执行时间,线程优先级,回调 callback 等;可以根据自己需要创建自己需要的线程池,一共有四种;线程异常时,可以打印异常日志,避免崩溃。
  • 关于线程池,对于开发来说是十分重要,但是又有点难以理解或者运用。关于写线程池的博客网上已经有很多了,但是一般很少有看到的实际案例或者封装的库,许多博客也仅仅是介绍了线程池的概念,方法,或者部分源码分析,那么为了方便管理线程任务操作,所以才想结合实际案例是不是更容易理解线程池,更多可以参考代码。

线程池封装库 GitHub 链接:https://github.com/yangchong2…

1. 遇到的问题和需求

1.1 遇到的问题有哪些?

  • 继承 Thread,或者实现接口 Runnable 来开启一个子线程,无法准确地知道线程什么时候执行完成并获得到线程执行完成后返回的结果
  • 当线程出现异常的时候,如何避免导致崩溃问题?博客

1.2 遇到的需求

  • 如何在实际开发中配置线程的优先级
  • 开启一个线程,是否可以监听 Runnable 接口中 run 方法操作的过程,比如监听线程的状态开始,成功,异常,完成等多种状态。
  • 开启一个线程,是否可以监听 Callable<T> 接口中 call()方法操作的过程,比如监听线程的状态开始,错误异常,完成等多种状态。

1.3 多线程通过实现 Runnable 弊端

  • 1.3.1 一般开启线程的操作如下所示

    new Thread(new Runnable() {
        @Override
        public void run() {// 做一些任务}
    }).start();
  • 创建了一个线程并执行,它在任务结束后 GC 会自动回收该线程。
  • 在线程并发不多的程序中确实不错,而假如这个程序有很多地方需要开启大量线程来处理任务,那么如果还是用上述的方式去创建线程处理的话,那么将导致系统的性能表现的非常糟糕。博客
  • 1.3.2 主要的弊端有这些,可能总结并不全面
  • 大量的线程创建、执行和销毁是非常耗 cpu 和内存的,这样将直接影响系统的吞吐量,导致性能急剧下降,如果内存资源占用的比较多,还很可能造成 OOM
  • 大量的线程的创建和销毁很容易导致 GC 频繁的执行,从而发生内存抖动现象,而发生了内存抖动,对于移动端来说,最大的影响就是造成界面卡顿
  • 线程的创建和销毁都需要时间,当有大量的线程创建和销毁时,那么这些时间的消耗则比较明显,将导致性能上的缺失

1.4 为什么要用线程池

  • 使用线程池管理线程优点

    • ①降低系统资源消耗,通过重用已存在的线程,降低线程创建和销毁造成的消耗;
    • ②提高系统响应速度,当有任务到达时,无需等待新线程的创建便能立即执行;
    • ③方便线程并发数的管控,线程若是无限制的创建,不仅会额外消耗大量系统资源,更是占用过多资源而阻塞系统或 oom 等状况,从而降低系统的稳定性。线程池能有效管控线程,统一分配、调优,提供资源使用率;
    • ④更强大的功能,线程池提供了定时、定期以及可控线程数等功能的线程池,使用方便简单。

1.5 线程池执行流程

  • 大概的流程图如下

  • 文字描述如下

    • ①如果在线程池中的线程数量没有达到核心的线程数量,这时候就回启动一个核心线程来执行任务。
    • ②如果线程池中的线程数量已经超过核心线程数,这时候任务就会被插入到任务队列中排队等待执行。
    • ③由于任务队列已满,无法将任务插入到任务队列中。这个时候如果线程池中的线程数量没有达到线程池所设定的最大值,那么这时候就会立即启动一个非核心线程来执行任务。
    • ④如果线程池中的数量达到了所规定的最大值,那么就会拒绝执行此任务,这时候就会调用 RejectedExecutionHandler 中的 rejectedExecution 方法来通知调用者。博客

2. 封装库具有的功能

2.1 常用的功能

  • 支持线程执行过程中状态回调监测(包含成功,失败,异常等多种状态)
  • 支持线程异常检测,并且可以打印异常日志
  • 支持设置线程属性,比如名称,延时时长,优先级,callback
  • 支持异步开启线程任务,支持监听异步回调监听
  • 方便集成,方便使用,可以灵活选择创建不同的线程池

3. 封装库的具体使用

3.1 一键集成

  • compile ‘cn.yc:YCThreadPoolLib:1.3.0’

3.2 在 application 中初始化库

  • 代码如下所示

    public class App extends Application{
    
        private static App instance;
        private PoolThread executor;
    
        public static synchronized App getInstance() {if (null == instance) {instance = new App();
            }
            return instance;
        }
    
        public App(){}
    
        @Override
        public void onCreate() {super.onCreate();
            instance = this;
            // 初始化线程池管理器
            initThreadPool();}
    
        /**
     */
    private void initThreadPool() {
        // 创建一个独立的实例进行使用
        executor = PoolThread.ThreadBuilder
                .createFixed(5)
                .setPriority(Thread.MAX_PRIORITY)
                .setCallback(new LogCallback())
                .build();}

    /**
     * 获取线程池管理器对象,统一的管理器维护所有的线程池
     * @return                      executor 对象
     */
    public PoolThread getExecutor(){return executor;}
}


// 自定义回调监听 callback,可以全局设置,也可以单独设置。都行
public class LogCallback implements ThreadCallback {

    private final String TAG = "LogCallback";

    @Override
    public void onError(String name, Throwable t) {Log.e(TAG, "LogCallback"+"------onError"+"-----"+name+"----"+Thread.currentThread()+"----"+t.getMessage());
    }

    @Override
    public void onCompleted(String name) {Log.e(TAG, "LogCallback"+"------onCompleted"+"-----"+name+"----"+Thread.currentThread());
    }

    @Override
    public void onStart(String name) {Log.e(TAG, "LogCallback"+"------onStart"+"-----"+name+"----"+Thread.currentThread());
    }
}
``` 

3.3 最简单的 runnable 线程调用方式

  • 关于设置 callback 回调监听,我这里在 app 初始化的时候设置了全局的 logCallBack,所以这里没有添加,对于每个单独的执行任务,可以添加独立 callback。

    PoolThread executor = App.getInstance().getExecutor();
            executor.setName("最简单的线程调用方式");
            executor.setDeliver(new AndroidDeliver());
            executor.execute(new Runnable() {
                @Override
                public void run() {Log.e("MainActivity","最简单的线程调用方式");
                }
            });

3.4 最简单的异步回调

  • 如下所示

    PoolThread executor = App.getInstance().getExecutor();
            executor.setName("异步回调");
            executor.setDelay(2,TimeUnit.MILLISECONDS);
            // 启动异步任务
            executor.async(new Callable<Login>(){
                @Override
                public Login call() throws Exception {
                    // 做一些操作
                    return null;
                }
            }, new AsyncCallback<Login>() {
                @Override
                public void onSuccess(Login user) {Log.e("AsyncCallback","成功");
                }
    
                @Override
                public void onFailed(Throwable t) {Log.e("AsyncCallback","失败");
                }
    
                @Override
                public void onStart(String threadName) {Log.e("AsyncCallback","开始");
                }
            });

4. 线程池封装思路介绍

4.1 自定义 Runnable 和自定义 Callable 类

  • 4.1.1 首先看看 Runnable 和 Callable 接口代码

    public interface Runnable {public void run();
    }
    
    public interface Callable<V> {V call() throws Exception;
    }
  • 4.1.2 Runnable 和 Callable 接口是干什么的
  • Runnable 从 JDK1.0 开始就有了,Callable 是在 JDK1.5 增加的。
  • Thread 调用了 Runnable 接口中的方法用来在线程中执行任务。Runnable 和 Callable 都代表那些要在不同的线程中执行的任务。
  • Thread 调用了 Runnable 接口中的方法用来在线程中执行任务。博客
  • 4.1.3 Runnable 和 Callable 接口有何区别
  • 它们的主要区别是 Callable 的 call() 方法可以返回值和抛出异常,而 Runnable 的 run() 方法没有这些功能。Callable 可以返回装载有计算结果的 Future 对象。博客
  • 比较两个接口,可以得出这样结论:
  • Callable 的任务执行后可返回值,而 Runnable 的任务是不能返回值的
  • call() 方法可以抛出异常,run()方法不可以的
  • 运行 Callable 任务可以拿到一个 Future 对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过 Future 对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果;
  • 4.1.4 自定义 Runnable 包装类,重点看 run 方法代码逻辑

    public final class RunnableWrapper implements Runnable {
    
        private String name;
        private CallbackDelegate delegate;
        private Runnable runnable;
        private Callable callable;
    
        public RunnableWrapper(ThreadConfigs configs) {
            this.name = configs.name;
            this.delegate = new CallbackDelegate(configs.callback, configs.deliver, configs.asyncCallback);
        }
    
        /**
         * 启动异步任务,普通的
         * @param runnable              runnable
     */
    public RunnableWrapper setRunnable(Runnable runnable) {
        this.runnable = runnable;
        return this;
    }

    /**
     * 异步任务,回调用于接收可调用任务的结果
     * @param callable              callable
     * @return                      对象
     */
    public RunnableWrapper setCallable(Callable callable) {
        this.callable = callable;
        return this;
    }

    /**
     * 自定义 xxRunnable 继承 Runnable,实现 run 方法
     * 详细可以看我的 GitHub:https://github.com/yangchong211
     */
    @Override
    public void run() {Thread current = Thread.currentThread();
        ThreadToolUtils.resetThread(current, name, delegate);
        // 开始
        delegate.onStart(name);
        // 注意需要判断 runnable,callable 非空
        // avoid NullPointException
        if (runnable != null) {runnable.run();
        } else if (callable != null) {
            try {Object result = callable.call();
                // 监听成功
                delegate.onSuccess(result);
            } catch (Exception e) {
                // 监听异常
                delegate.onError(name, e);
            }
        }
        // 监听完成
        delegate.onCompleted(name);
    }
}
``` 

  • 4.1.5 自定义 Callable<T> 包装类,重点看 call 方法代码逻辑

    public final class CallableWrapper<T> implements Callable<T> {
    
        private String name;
        private ThreadCallback callback;
        private Callable<T> proxy;
    
        /**
         * 构造方法
         * @param configs               thread 配置,主要参数有:线程 name,延迟 time,回调 callback,异步 callback
     */
    public CallableWrapper(ThreadConfigs configs, Callable<T> proxy) {
        this.name = configs.name;
        this.proxy = proxy;
        this.callback = new CallbackDelegate(configs.callback, configs.deliver, configs.asyncCallback);
    }

    /**
     * 详细可以看我的 GitHub:https://github.com/yangchong211
     * 自定义 Callable 继承 Callable<T> 类,Callable 是在 JDK1.5 增加的。* Callable 的 call() 方法可以返回值和抛出异常
     * @return                      泛型
     * @throws Exception            异常
     */
    @Override
    public T call() {ThreadToolUtils.resetThread(Thread.currentThread(),name,callback);
        if (callback != null) {
            // 开始
            callback.onStart(name);
        }
        T t = null;
        try {t = proxy == null ? null : proxy.call();
        } catch (Exception e) {e.printStackTrace();
            // 异常错误
            if(callback!=null){callback.onError(name,e);
            }
        }finally {
            // 完成
            if (callback != null)  {callback.onCompleted(name);
            }
        }
        return t;
    }
}
``` 



4. 添加回调接口 AsyncCallback 和 ThreadCallback

  • 注意,这个写的自定义 callback,需要添加多种状态,你可以自定义其他状态。看完了这里再回过头看看 RunnableWrapper 中 run 方法和 CallableWrapper 中 call 方法的逻辑。博客
  • 4.0 为什么要这样设计
  • 它可以让程序员准确地知道线程什么时候执行完成并获得到线程执行完成后返回的结果。
  • AsyncCallback,在这个类中,可以看到三种状态[这个是在自定义 Runnable 中的 run 方法中实现],并且成功时可以携带结果,在异常时还可以打印异常日志。
  • ThreadCallback,在这个类中,可以看到三种状态[这个是在自定义 Callable<T> 中的 call 方法中实现],并且在异常时可以打印异常日志,在开始和完成时可以打印线程名称
  • 4.1 AsyncCallback 类代码如下所示

    /**
     * <pre>
     *     @author      杨充
     *     blog         https://www.jianshu.com/p/53017c3fc75d
     *     time
     *     desc         异步 callback 回调接口
     *     revise
     *     GitHub       https://github.com/yangchong211
 */
public interface AsyncCallback<T> {

    /**
     * 成功时调用
     * @param t         泛型
     */
    void onSuccess(T t);

    /**
     * 异常时调用
     * @param t         异常
     */
    void onFailed(Throwable t);

    /**
     * 通知用户任务开始运行
     * @param threadName            正在运行线程的名字
     */
    void onStart(String threadName);
}
``` 
  • 4.2 ThreadCallback 类代码如下所示

    /**
     * <pre>
     *     @author: yangchong
     *     blog  : https://github.com/yangchong211
     *     time  :
     *     desc  : 一个回调接口,用于通知用户任务的状态回调委托类
     *             线程的名字可以自定义
     *     revise:
 */
public interface ThreadCallback {

    /**
     * 当线程发生错误时,将调用此方法。* @param threadName            正在运行线程的名字
     * @param t                     异常
     */
    void onError(String threadName, Throwable t);

    /**
     * 通知用户知道它已经完成
     * @param threadName            正在运行线程的名字
     */
    void onCompleted(String threadName);

    /**
     * 通知用户任务开始运行
     * @param threadName            正在运行线程的名字
     */
    void onStart(String threadName);
}
``` 

4.3 创建线程池配置文件

  • 为什么要添加配置文件,配置文件的作用主要是存储当前任务的某些配置,比如线程的名称,回调 callback 等等这些参数。还可以用于参数的传递!

    public final class ThreadConfigs {
        /**
         * 线程的名称
     */
    public String name;
    /**
     * 线程执行延迟的时间
     * 通过 setDelay 方法设置
     */
    public long delay;
    /**
     * 线程执行者
     * JAVA 或者 ANDROID
     */
    public Executor deliver;
    /**
     * 用户任务的状态回调 callback
     */
    public ThreadCallback callback;
    /**
     * 异步 callback 回调 callback
     */
    public AsyncCallback asyncCallback;
}
``` 

4.4 创建 java 平台和 android 平台消息器 Executor

  • 在 android 环境下,想一想 callback 回调类中的几个方法,比如回调失败,回调成功,或者回调完成,可能会做一些操作 UI 界面的操作逻辑,那么都知道子线程是不能更新 UI 的,所以必须放到主线程中操作。
  • 但是在 Java 环境下,回调方法所运行的线程与任务执行线程其实可以保持一致。
  • 因此,这里需要设置该消息器用来区别回调的逻辑。主要作用是指定回调任务需要运行在什么线程之上。
  • 4.4.1 android 环境下

  • 4.4.2 java 环境下

  • 4.4.3 如何判断环境是 java 环境还是 Android 环境呢

    public final class ThreadToolUtils {/**
     */
    public static boolean isAndroid;
    /*
     * 静态代码块
     * 判断是否是 android 环境
     * Class.forName(xxx.xx.xx) 返回的是一个类对象
     * 首先要明白在 java 里面任何 class 都要装载在虚拟机上才能运行。*/
    static {
        try {Class.forName("android.os.Build");
            isAndroid = true;
        } catch (Exception e) {isAndroid = false;}
    }
}
``` 

4.5 创建 PoolThread 继承 Executor

  • 这里只是展示部分代码,如果想看完整的代码,可以直接看案例。博客
  • 4.5.1 继承 Executor 接口,并且实现 execute 方法

    public final class PoolThread implements Executor{
    
       
        /**
         * 启动任务
         * 这个是实现接口 Executor 中的 execute 方法
         * 提交任务无返回值
     */
    @Override
    public void execute (@NonNull Runnable runnable) {
        // 获取线程 thread 配置信息
        ThreadConfigs configs = getLocalConfigs();
        // 设置 runnable 任务
        runnable = new RunnableWrapper(configs).setRunnable(runnable);
        // 启动任务
        DelayTaskDispatcher.get().postDelay(configs.delay, pool, runnable);
        // 重置线程 Thread 配置
        resetLocalConfigs();}

    /**
     * 当启动任务或者发射任务之后需要调用该方法
     * 重置本地配置,置 null
     */
    private synchronized void resetLocalConfigs() {local.set(null);
    }


    /**
     * 注意需要用 synchronized 修饰,解决了多线程的安全问题
     * 获取本地配置参数
     * @return
     */
    private synchronized ThreadConfigs getLocalConfigs() {ThreadConfigs configs = local.get();
        if (configs == null) {configs = new ThreadConfigs();
            configs.name = defName;
            configs.callback = defCallback;
            configs.deliver = defDeliver;
            local.set(configs);
        }
        return configs;
    }

}
``` 


4.6 使用 builder 模式获取线程池对象

  • 4.6.1 看下 builder 模式下代码
  • 如果还不是很熟悉 builder 模式,欢迎阅读我的另外一篇文章之——设计模式之二:Builder 模式:https://www.jianshu.com/p/246…
  • 也可以看 Android 源码设计模式这本书,很不错
  • 直接列出代码,如下所示:

    public final class PoolThread implements Executor{
    
        // 省略部分代码……
       
        public static class ThreadBuilder {
    
            final static int TYPE_CACHE = 0;
            final static int TYPE_FIXED = 1;
            final static int TYPE_SINGLE = 2;
            final static int TYPE_SCHEDULED = 3;
    
            int type;
            int size;
            int priority = Thread.NORM_PRIORITY;
            String name;
            ThreadCallback callback;
            Executor deliver;
            ExecutorService pool;
    
            private ThreadBuilder(int size,  int type, ExecutorService pool) {this.size = Math.max(1, size);
                this.type = type;
                this.pool = pool;
            }
    
            /**
             * 通过 Executors.newSingleThreadExecutor()创建线程池
         */
        public static ThreadBuilder create(ExecutorService pool) {return new ThreadBuilder(1, TYPE_SINGLE, pool);
        }

        /**
         * 通过 Executors.newCachedThreadPool()创建线程池
         * 它是一个数量无限多的线程池,都是非核心线程,适合执行大量耗时小的任务
         */
        public static ThreadBuilder createCacheable() {return new ThreadBuilder(0, TYPE_CACHE, null);
        }

        /**
         * 通过 Executors.newFixedThreadPool()创建线程池
         * 线程数量固定的线程池,全部为核心线程,响应较快,不用担心线程会被回收。*/
        public static ThreadBuilder createFixed(int size) {return new ThreadBuilder(size, TYPE_FIXED, null);
        }

        /**
         * 通过 Executors.newScheduledThreadPool()创建线程池
         * 有数量固定的核心线程,且有数量无限多的非核心线程,适合用于执行定时任务和固定周期的重复任务
         */
        public static ThreadBuilder createScheduled(int size) {return new ThreadBuilder(size, TYPE_SCHEDULED, null);
        }

        /**
         * 通过 Executors.newSingleThreadPool()创建线程池
         * 内部只有一个核心线程,所有任务进来都要排队按顺序执行
         * 和 create 区别是 size 数量
         */
        public static ThreadBuilder createSingle() {return new ThreadBuilder(0, TYPE_SINGLE, null);
        }

        /**
         * 将默认线程名设置为“已使用”。*/
        public ThreadBuilder setName (@NonNull String name) {if (name.length()>0) {this.name = name;}
            return this;
        }

        /**
         * 将默认线程优先级设置为“已使用”。*/
        public ThreadBuilder setPriority (int priority) {
            this.priority = priority;
            return this;
        }

        /**
         * 将默认线程回调设置为“已使用”。*/
        public ThreadBuilder setCallback (ThreadCallback callback) {
            this.callback = callback;
            return this;
        }

        /**
         * 设置默认线程交付使用
         */
        public ThreadBuilder setDeliver(Executor deliver) {
            this.deliver = deliver;
            return this;
        }

        /**
         * 创建用于某些配置的线程管理器。* @return                  对象
         */
        public PoolThread build () {
            // 最大值
            priority = Math.max(Thread.MIN_PRIORITY, priority);
            // 最小值
            priority = Math.min(Thread.MAX_PRIORITY, priority);

            size = Math.max(1, size);
            if (name==null || name.length()==0) {
                // 如果没有设置名字,那么就使用下面默认的线程名称
                switch (type) {
                    case TYPE_CACHE:
                        name = "CACHE";
                        break;
                    case TYPE_FIXED:
                        name = "FIXED";
                        break;
                    case TYPE_SINGLE:
                        name = "SINGLE";
                        break;
                    default:
                        name = "POOL_THREAD";
                        break;
                }
            }

            if (deliver == null) {if (ThreadToolUtils.isAndroid) {deliver = AndroidDeliver.getInstance();
                } else {deliver = JavaDeliver.getInstance();
                }
            }
            return new PoolThread(type, size, priority, name, callback, deliver, pool);
        }
    }
}
``` 

  • 4.6.2 添加设置 thread 配置信息的方法

    /**
     * 为当前的任务设置线程名。* @param name              线程名字
 */
public PoolThread setName(String name) {getLocalConfigs().name = name;
    return this;
}


/**
 * 设置当前任务的线程回调,如果未设置,则应使用默认回调。* @param callback          线程回调
 * @return                  PoolThread
 */
public PoolThread setCallback (ThreadCallback callback) {getLocalConfigs().callback = callback;
    return this;
}

/**
 * 设置当前任务的延迟时间.
 * 只有当您的线程池创建时,它才会产生效果。* @param time              时长
 * @param unit              time unit
 * @return                  PoolThread
 */
public PoolThread setDelay (long time, TimeUnit unit) {long delay = unit.toMillis(time);
    getLocalConfigs().delay = Math.max(0, delay);
    return this;
}

/**
 * 设置当前任务的线程传递。如果未设置,则应使用默认传递。* @param deliver           thread deliver
 * @return                  PoolThread
 */
public PoolThread setDeliver(Executor deliver){getLocalConfigs().deliver = deliver;
    return this;
}
``` 


  • 4.6.3 看下 builder 模式下创建对象的代码
  • 通过调用 ThreadBuilder 类中的 build()方法创建属于自己的线程池。
  • 最后通过 new PoolThread(type, size, priority, name, callback, deliver, pool)创建对象,并且作为返回值返回。
  • 然后再来看看 PoolThread 方法,这部分看目录 4.7 部分介绍。博客

4.7 灵活创建线程池[重点]

  • 4.7.1 创建线程池的五种方法
  • 通过 Executors 的工厂方法获取这五种线程池
  • 通过 Executors 的工厂方法来创建线程池极其简便,其实它的内部还是通过 new ThreadPoolExecutor(…)的方式创建线程池的,具体可以看看源码,这里省略呢……

    ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
    ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
    ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
    ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
    ScheduledExecutorService singleThreadScheduledPool = Executors.newSingleThreadScheduledExecutor();
  • 4.7.2 灵活创建不同类型线程池
  • 设计的时候,希望能够选择性地创建自己想要的线程池,并且动态设置线程的数量,还可以设置线程优先级。
  • 4.7.2.1 创建不同类型线程池代码如下所示:

    /**
     * 创建线程池,目前支持以下四种
     * @param type                  类型
     * @param size                  数量 size
     * @param priority              优先级
 */
private ExecutorService createPool(int type, int size, int priority) {switch (type) {
        case Builder.TYPE_CACHE:
            // 它是一个数量无限多的线程池,都是非核心线程,适合执行大量耗时小的任务
            return Executors.newCachedThreadPool(new DefaultFactory(priority));
        case Builder.TYPE_FIXED:
            // 线程数量固定的线程池,全部为核心线程,响应较快,不用担心线程会被回收。return Executors.newFixedThreadPool(size, new DefaultFactory(priority));
        case Builder.TYPE_SCHEDULED:
            // 有数量固定的核心线程,且有数量无限多的非核心线程,适合用于执行定时任务和固定周期的重复任务
            return Executors.newScheduledThreadPool(size, new DefaultFactory(priority));
        case Builder.TYPE_SINGLE:
        default:
            // 内部只有一个核心线程,所有任务进来都要排队按顺序执行
            return Executors.newSingleThreadExecutor(new DefaultFactory(priority));
    }
}
``` 

  • 4.7.2.1 了解一下 ThreadFactory 接口作用
  • 关于 ThreadFactory 接口的源代码如下所示:
  • 以看到 ThreadFactory 中,只有一个 newThread 方法,它负责接收一个 Runnable 对象,并将其封装到 Thread 对象中,进行执行。
  • 通过有道词典对这个类的说明进行翻译是:根据需要创建新线程的对象。使用线程工厂可以消除对 {@link Thread#Thread(Runnable) 新线程}的硬连接,从而使应用程序能够使用特殊的线程子类、优先级等。

    public interface ThreadFactory {
    
        /**
         * Constructs a new {@code Thread}.  Implementations may also initialize
     *
     * @param r a runnable to be executed by new thread instance
     * @return constructed thread, or {@code null} if the request to
     *         create a thread is rejected
     */
    Thread newThread(Runnable r);
}
``` 


  • 4.7.2.3 创建默认 MyThreadFactory 继承 ThreadFactory
  • 创建一个默认的 MyThreadFactory,并且这个类继承 ThreadFactory,实现接口中的 newThread 方法。然后在 newThread 方法中创建线程,并且设置线程优先级。
  • 创建一个优先级线程池非常有用,它可以在线程池中线程数量不足或系统资源紧张时,优先处理我们想要先处理的任务,而优先级低的则放到后面再处理,这极大改善了系统默认线程池以 FIFO 方式处理任务的不灵活。
  • 代码如下所示

    public class MyThreadFactory implements ThreadFactory {
    
        private int priority;
        public MyThreadFactory(int priority) {this.priority = priority;}
    
        @Override
        public Thread newThread(@NonNull Runnable runnable) {Thread thread = new Thread(runnable);
            thread.setPriority(priority);
            return thread;
        }
    
    }

4.8 启动线程池中的任务

  • 具体逻辑看 DelayTaskExecutor 中的 postDelay 方法

    /**
     * 启动
     * @param delay                     延迟执行的时间,注意默认单位是 TimeUnit.MILLISECONDS
     * @param pool                      pool 线程池
 */
void postDelay(long delay, final ExecutorService pool, final Runnable task) {if (delay == 0) {
        // 如果时间是 0,那么普通开启
        pool.execute(task);
        return;
    }

    // 延时操作
    dispatcher.schedule(new Runnable() {
        @Override
        public void run() {
            // 在将来的某个时间执行给定的命令。该命令可以在新线程、池线程或调用线程中执行
            pool.execute(task);
        }
    }, delay, TimeUnit.MILLISECONDS);
}
``` 


4.9 如何关闭线程池操作

  • 代码如下所示

    /**
 */
public void stop(){
    try {
        // shutdown 只是起到通知的作用
        // 只调用 shutdown 方法结束线程池是不够的
        pool.shutdown();
        // (所有的任务都结束的时候,返回 TRUE)
        if(!pool.awaitTermination(0, TimeUnit.MILLISECONDS)){// 超时的时候向线程池中所有的线程发出中断(interrupted)。pool.shutdownNow();}
    } catch (InterruptedException e) {
        // awaitTermination 方法被中断的时候也中止线程池中全部的线程的执行。e.printStackTrace();} finally {pool.shutdownNow();
    }
}
```



5. 其他介绍

5.1 参考的开源案例

  • Thread,Java 多线程核心技术:https://github.com/boostorg/t…
  • threadpool4j:https://github.com/aofeng/thr…
  • TickThreading:https://github.com/MinimallyC…
  • MOBIN-F:https://github.com/MOBIN-F/Th…
  • litesuits:https://github.com/litesuits/…

5.2 参考的博客

  • 自己封装的简单 ThreadFactory:https://blog.csdn.net/hbdatou…
  • Android 多线程 线程池原理 封装线程池:https://blog.csdn.net/xiangyu…
  • Android 自定义线程池的实战:https://www.cnblogs.com/zhaoy…
  • android 线程及线程池:https://www.jianshu.com/p/d79…

其他介绍

01. 关于博客汇总链接

  • 1. 技术博客汇总
  • 2. 开源项目汇总
  • 3. 生活博客汇总
  • 4. 喜马拉雅音频汇总
  • 5. 其他汇总

02. 关于我的博客

  • github:https://github.com/yangchong211
  • 知乎:https://www.zhihu.com/people/…
  • 简书:http://www.jianshu.com/u/b7b2…
  • csdn:http://my.csdn.net/m0_37700275
  • 喜马拉雅听书:http://www.ximalaya.com/zhubo…
  • 开源中国:https://my.oschina.net/zbj161…
  • 泡在网上的日子:http://www.jcodecraeer.com/me…
  • 邮箱:yangchong211@163.com
  • 阿里云博客:https://yq.aliyun.com/users/a… 239.headeruserinfo.3.dT4bcV
  • segmentfault 头条:https://segmentfault.com/u/xi…
  • 掘金:https://juejin.im/user/593943…

线程池封装库 GitHub 链接:https://github.com/yangchong2…

正文完
 0