警告:我不做 Android 工作,所以我不是那里的专家。我在这里的观点是基于对 Android 文档的快速阅读。
tl;博士
➥ 使用执行器而不是HandlerThread。
Executors 框架比 HandlerThread 使用的旧版 Thread 工具更现代、更灵活、更强大。你可以在 HandlerThread 中做的所有事情,你都可以用 executors 做得更好。
区别
HandlerThread 和ThreadPoolExecutor 之间的一大区别是前者来自 Android,而后者来自 Java。因此,如果您将使用 Java 进行其他工作,您可能不想养成使用 HandlerThread 的习惯。
另一个很大的区别是年龄。 android.os.HandlerThread 类继承自 java.lang.Thread,并且可以追溯到最初的 Android API 级别 1。虽然在当时还不错,但 Java 中的 Thread 工具在设计上是有限的。后来的 Java 中更现代、更灵活、更强大的 Executors framework 取代了该工具。
执行者
您的问题不清楚这些是经常性工作还是偶尔安排的工作。两者都可以用 Executors 处理。
对于在特定时间运行一次的作业以及重复的计划作业,请使用ScheduledExecutorService。您可以通过指定延迟(等待执行的时间跨度)来安排作业在特定时间运行一次。对于重复的作业,您可以指定等待、然后运行、然后等待、然后运行等的数量。我不会进一步解决这个问题,因为您似乎在谈论零星的直接工作,而不是计划或重复的工作。如果有兴趣,请搜索 Stack Overflow,因为 ScheduledExecutorService 已经在 Stack Overflow 上被报道过很多次了。
单线程池
HandlerThread是一个创建单线程的结构
如果您想重新创建该单线程行为,请使用 thread pool consisting of only a single thread。
ExecutorService es = Executors.newSingleThreadExecutor() ;
完成你的任务。实现Runnable 或Callable,使用 (a) 实现任一接口的类,(b) 不定义类,通过 lambda 语法或常规语法。
常规语法。
Runnable sayHelloJob = new Runnable()
{
@Override
public void run ( )
{
System.out.println( "Hello. " + Instant.now() );
}
};
Lambda 语法。
Runnable sayBonjourJob = ( ) -> System.out.println( "Bonjour. " + Instant.now() );
根据需要向执行器服务提交尽可能多的这些作业。
es.submit( sayHelloJob ) ;
es.submit( sayBonjourJob ) ;
注意submit 方法返回一个Future。使用Future 对象,您可以检查计算是否完成、等待其完成或检索计算结果。或者您可以选择忽略上面代码中看到的Future 对象。
固定线程池
如果您想要多线程行为,只需使用不同类型的线程池创建您的执行程序。
fixed thread pool 具有服务于单个提交作业队列(Runnable 或 Callable 对象)的最大线程数。线程继续存在,并在发生故障时根据需要进行替换。
ExecutorService es = Executors.newFixedThreadPool( 3 ) ; // Specify number of threads.
其余代码保持不变。这就是使用ExecutorService 接口的美妙之处:您可以更改执行器服务的实现以获得不同的行为,同时不会破坏调用该执行器服务的代码。
缓存线程池
cached thread pool 可能会为您提供更好的服务。不像固定线程池那样立即创建和维护一定数量的线程,这个池只在需要时创建线程,最多创建线程。当一个线程完成并休息一分钟以上时,该线程被终止。正如 Javadoc 所指出的,这对于诸如您的“许多短期异步任务”是理想的。但请注意,可以同时运行的线程没有上限。如果您的应用程序的性质使得您可能经常看到许多作业同时到达的高峰,您可能希望使用缓存线程池以外的其他实现。
ExecutorService es = Executors.newCachedThreadPool() ;
管理执行器和线程
但由于线程过多,可能会出现泄漏,甚至内存不足
程序员和系统管理员的工作是不让生产服务器负担过重。您需要监控生产中的性能。管理很容易执行,因为您可以控制支持执行程序服务的线程池中可用的线程数。
我们有一个应用程序,它使用前台服务并永远保持活力
当然,您的应用最终会结束,即关闭。发生这种情况时,请务必关闭执行程序及其支持线程池。否则线程可能会存活下来,并无限期地继续下去。请务必使用应用执行环境的生命周期挂钩来检测应用关闭并对其做出反应。
作业计数可能是 5 个,也可能是 20 个,具体取决于用户以某种方式执行的操作。
提交给执行器服务的作业会被缓冲,直到它们可以被安排在线程上执行。因此,您可能有一个线程池,例如,3 个线程和 20 个等待作业。没问题。等待的作业最终将在它们的时间到来时被执行。
您可能希望优先处理某些工作,以便在优先级较低的工作之前完成。一种简单的方法是拥有 两个 执行器服务。每个执行器都有自己的后台线程池。一个执行器用于较少但优先级较高的作业,而另一个执行器用于许多较低优先级的作业。
请记住,线程池中的线程在待机时不做任何工作,在 Java 中对于 CPU 或内存而言几乎没有开销。因此,让一个特殊的、优先级更高的执行器服务坐在那里等待最终的工作到达没有任何不利之处。唯一需要担心的是,您的所有后台线程总数及其工作负载不会使您的机器不堪重负。此外,线程池的实现很可能会在闲置一段时间后关闭未使用的线程。