多线程3,线程池封装库

目录介绍

  • 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 添加回调接口AsyncCallback和ThreadCallback
  • 4.3 创建线程池配置文件
  • 4.4 创建java平台和android平台消息器Executor
  • 4.5 创建PoolThread继承Executor
  • 4.6 使用builder模式获取线程池对象
  • 4.7 灵活创建线程池[重点]
  • 4.8 启动线程池中的任务
  • 5.其他介绍
  • 5.1 参考的开源案例
  • 5.2 参考的博客
  • 5.3 我的博客

好消息

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

0.前言介绍

0.1 基础介绍

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

  • 代码位置:https://github.com/yangchong211/YCThreadPool

0.2 其他相关

0.3 其他相关开源介绍

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 为什么要用线程池

  • 重用线程池中的线程,避免频繁地创建和销毁线程带来的性能消耗;
  • 有效控制线程的最大并发数量,防止线程过大导致抢占资源造成系统阻塞;
  • 可以对线程进行一定地管理。

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
     * @return                      对象
     */
    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
     * @param proxy                 线程优先级
     */
    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
 * </pre>
 */
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:
 * </pre>
 */
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 {
    /**
     * 线程的名称
     * 通过setName方法设置
     */
    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环境下
    image
  • 4.4.2 java环境下
    image
  • 4.4.3 如何判断环境是java环境还是Android环境呢
public final class ThreadToolUtils {

    /**
     * 标志:是否在android平台上
     */
    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方法
     * 提交任务无返回值
     * @param runnable              task,注意添加非空注解
     */
    @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/246b01ca84c2
  • 也可以看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              线程名字
 * @return                  PoolThread
 */
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              优先级
 * @return
 */
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
     * priority, name, daemon status, {@code ThreadGroup}, etc.
     *
     * @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线程池
 * @param task                      runnable
 */
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);
}

5.其他介绍

5.1 参考的开源案例

5.2 参考的博客

5.3 我的个人博客

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容

  • 先看几个概念:线程:进程中负责程序执行的执行单元。一个进程中至少有一个线程。多线程:解决多任务同时执行的需求,合理...
    yeying12321阅读 527评论 0 0
  • 一.Java中的ThreadPoolExecutor类 java.uitl.concurrent.ThreadPo...
    谁在烽烟彼岸阅读 625评论 0 0
  • 众所周知,家访是学校与家庭共同教育好孩子的一道不可或缺的桥梁。通过家访能及时了解学生在家学习和生活的情况以及思想动...
    赵超198165阅读 538评论 0 0
  • 时间过得真快,15天的行动营转眼就要结束了,在这次行动营打卡活动中能够顺利完成打卡任务,为此我感到很是自豪,201...
    唯其时物阅读 319评论 0 2
  • 本文参加#感悟三下乡,青春筑梦行#活动,本人承诺,文章内容为原创,且未在其他平台发表过 怀着满腔热血,期待...
    晚来晴阅读 533评论 0 0