目录介绍

  • 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 我的博客

0.前言介绍

0.1 基础介绍

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

0.2 其他相关

  • 关于线程池关联博客有:
  • 多线程1,线程基础知识
  • 多线程2,线程池深入理解
  • 多线程3,线程池封装库
  • 如果觉得前两篇线程知识太基础,可以直接忽略……主要是回顾基础知识点!

0.3 其他相关开源介绍

  • 技术博客大汇总:https://www.jianshu.com/p/53017c3fc75d
  • 生活博客大汇总:https://www.jianshu.com/p/ed6a5d3ef3e8
  • 开源项目库大汇总:https://github.com/yangchong211

1.遇到的问题和需求

1.1 遇到的问题有哪些?

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

1.2 遇到的需求

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

1.3 多线程通过实现Runnable弊端

  • 1.3.1 一般开启线程的操作如下所示
new Thread(new Runnable() {@Overridepublic 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(){}@Overridepublic 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";@Overridepublic void onError(String name, Throwable t) {Log.e(TAG, "LogCallback"+"------onError"+"-----"+name+"----"+Thread.currentThread()+"----"+t.getMessage());}@Overridepublic void onCompleted(String name) {Log.e(TAG, "LogCallback"+"------onCompleted"+"-----"+name+"----"+Thread.currentThread());}@Overridepublic 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() {@Overridepublic void run() {Log.e("MainActivity","最简单的线程调用方式");}});

3.4 最简单的异步回调

PoolThread executor = App.getInstance().getExecutor();executor.setName("异步回调");executor.setDelay(2,TimeUnit.MILLISECONDS);// 启动异步任务executor.async(new Callable<Login>(){@Overridepublic Login call() throws Exception {// 做一些操作return null;}}, new AsyncCallback<Login>() {@Overridepublic void onSuccess(Login user) {Log.e("AsyncCallback","成功");}@Overridepublic void onFailed(Throwable t) {Log.e("AsyncCallback","失败");}@Overridepublic 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*/@Overridepublic void run() {Thread current = Thread.currentThread();ThreadToolUtils.resetThread(current, name, delegate);//开始delegate.onStart(name);//注意需要判断runnable,callable非空// avoid NullPointExceptionif (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包装类,重点看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            异常*/@Overridepublic 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中的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环境下
  • 4.4.2 java环境下
  • 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,注意添加非空注解*/@Overridepublic 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;}@Overridepublic 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() {@Overridepublic void run() {//在将来的某个时间执行给定的命令。该命令可以在新线程、池线程或调用线程中执行pool.execute(task);}}, delay, TimeUnit.MILLISECONDS);
}

5.其他介绍

5.1 参考的开源案例

  • Thread,Java多线程核心技术:https://github.com/boostorg/thread
  • alibaba,巴transmittable-thread-local:https://github.com/alibaba/transmittable-thread-local
  • threadpool4j:https://github.com/aofeng/threadpool4j
  • TickThreading:https://github.com/MinimallyCorrect/TickThreading
  • yjfnypeu:https://github.com/yjfnypeu/EasyThread
  • MOBIN-F:https://github.com/MOBIN-F/Thread

5.2 参考的博客

  • 自己封装的简单ThreadFactory:https://blog.csdn.net/hbdatouerzi/article/details/73715668
  • Android 多线程 线程池原理 封装线程池:https://blog.csdn.net/xiangyunwan/article/details/72550948
  • Android 自定义线程池的实战:https://www.cnblogs.com/zhaoyanjun/p/5761776.html
  • android线程及线程池:https://www.jianshu.com/p/d79dab197d5a

5.3 我的个人博客

  • github: https://github.com/yangchong211
  • 知乎: https://www.zhihu.com/people/yang-chong-69-24/pins/posts
  • 简书: http://www.jianshu.com/u/b7b2c6ed9284
  • csdn: http://my.csdn.net/m0_37700275
  • 喜马拉雅听书: http://www.ximalaya.com/zhubo/71989305/
  • 泡在网上的日子:http://www.jcodecraeer.com/member/content_list.php?channelid=1
  • 邮箱:yangchong211@163.com
  • 阿里云博客:https://yq.aliyun.com/users/article?spm=5176.100239.headeruserinfo.3.dT4bcV

多线程3,线程池封装库相关推荐

  1. android 多线程封装,Android线程池封装库

    目录介绍 1.遇到的问题和需求 1.1 遇到的问题有哪些 1.2 遇到的需求 1.3 多线程通过实现Runnable弊端 1.4 为什么要用线程池 2.封装库具有的功能 2.1 常用的功能 3.封装库 ...

  2. Android线程池封装库

    目录介绍 1.遇到的问题和需求 1.1 遇到的问题有哪些 1.2 遇到的需求 1.3 多线程通过实现Runnable弊端 1.4 为什么要用线程池 2.封装库具有的功能 2.1 常用的功能 3.封装库 ...

  3. pool python 传参数_Python-爬虫-多线程、线程池模拟(urllib、requests、UserAgent、超时等)...

    接着之前的MonkeyLei:Python-爬取页面内容(涉及urllib.requests.UserAgent.Json等) 继续练习下多线程,线程池模拟.. 我想这样: 1. 创建一个线程池,线程 ...

  4. C++多线程以及线程池

    1 线程 1.1 简介   线程(英语:thread)是操作系统能够进行运算调度的最小单位.大部分情况下,它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程 ...

  5. java多线程及线程池使用

    Java多线程及线程池的使用 Java多线程 一.Java多线程涉及的包和类 二.Java创建多线程的方式 三.Java线程池 1. 创建线程池ThreadPoolExecutor的7个参数 2. 线 ...

  6. 用10086客服热线理解Java高级多线程之线程池

    Java高级多线程之线程池 客服热线案例 引入线程池 1.线程的概念 2.线程池的作用: 获取线程池 1.常用的线程池接口和类 2.代码案例 Callable接口 1.概念简述 2.应用场景 3.方法 ...

  7. Java的多线程和线程池的使用,你真的清楚了吗?

    Java的多线程和线程池的使用 多线程大大提高程序运行效率,我们在开发过程中经常会开启一个线程来执行一些费时的任务.开启一个线程有4种方式,在下面的文章我将详细的去讲解. 继承Thread 继承Thr ...

  8. 多线程之线程池-各个参数的含义- 阿里,美团,京东面试题目

    阿里的面试官问了个问题,如果corepollSize=10,MaxPollSize=20,如果来了25个线程 怎么办, 答案: 当一个任务通过execute(Runnable)方法欲添加到线程池时: ...

  9. Java多线程之线程池配置合理线程数

    Java多线程之线程池配置合理线程数 目录 代码查看公司服务器或阿里云是几核的 合理线程数配置之CPU密集型 合理线程数配置之IO密集型 1. 代码查看公司服务器或阿里云是几核的 要合理配置线程数首先 ...

  10. Java多线程之线程池的手写改造和拒绝策略

    Java多线程之线程池的手写改造和拒绝策略 目录 自定义线程池的使用 四种拒绝策略代码体现 1. 自定义线程池的使用 自定义线程池(拒绝策略默认AbortPolicy) public class My ...

最新文章

  1. web记录文章浏览数_内网渗透 -- 获取内网浏览器历史记录等相关信息
  2. 请问如何更改dedecms“文件保存目录”的字符限制 ?
  3. oppo n1t android 版本,OPPO N1的手机系统是什么?OPPO N1能升级安卓4.3吗?
  4. (填坑:SQL打印两次)mybatisplus+p6spy 日志打印
  5. Informatica PowerCenter使用介绍-转载
  6. cygwin编译生成hello world_RISC-V 入门 Part4: 编译、链接、加载
  7. 跳一跳201803-1
  8. 深度解析:会用Excel,还有必要学Python吗?
  9. java实现线程的方式_java多线程实现的四种方式
  10. 2018-04-22接口自动化测试学习心得(1)
  11. 解决win10系统下,git Bash闪退的问题
  12. 网站随机背景音乐源码
  13. 【作者解读】ERNIE-GEN : 原来你是这样的生成预训练框架!
  14. flash与javacript通信(1)
  15. Ubuntu下配置D-Link路由器进行联网
  16. buildroot 下载和make nconfig
  17. 数据结构上机实践第八周项目3-顺序串算法
  18. 数据库——mysql如何获取当前时间---https://www.cnblogs.com/Chenshuai7/p/5136469.html
  19. 毕设题目:Matlab图像重建
  20. 鲸云效解读A/B测试,get这一篇就够了

热门文章

  1. Sublim text3汉化
  2. 关于IDE集成开发环境,Pycharm小技巧
  3. NLTK学习笔记(六):利用机器学习进行文本分类
  4. No.025:Reverse Nodes in k-Group
  5. nginx root alias 文件路径配置
  6. Eclipse上索爱手机开发平台的配置
  7. web.config学习资料
  8. 深度解析vue.js响应式原理解析与实现
  9. laravel连接多个不同数据库的单例类
  10. sqlserver 游标写法