new Thread(task).start() VS ThreadPoolExecutor.submit(task) in Android

在我的Android项目中,我有很多地方需要异步运行一些代码(Web请求,对db的调用等)。这不是长时间运行的任务(最多几秒钟)。直到现在,我还在做这种事情,创建一个新线程,通过任务传递一个新的可运行项。但最近我读了一篇关于Java中的线程和并发性的文章,并了解到为每个任务创建一个新的线程不是一个好的决定。

所以现在我已经在我的班级中创建了一个包含5个线程的线程。代码如下:ThreadPoolExecutorApplication

public class App extends Application {

    private ThreadPoolExecutor mPool;

    @Override
    public void onCreate() {
        super.onCreate();

        mPool =  (ThreadPoolExecutor)Executors.newFixedThreadPool(5);
    }
}

我还有一个将Runnable任务提交给执行器的方法:

public void submitRunnableTask(Runnable task){
    if(!mPool.isShutdown() && mPool.getActiveCount() != mPool.getMaximumPoolSize()){
        mPool.submit(task);
    } else {
        new Thread(task).start();
    }
}

因此,当我想在代码中运行异步任务时,我会获取实例并调用将runnable传递给它的方法。如您所见,我还检查了线程池是否有空闲线程来执行我的任务,如果没有,我将创建一个新的线程(我不认为这会发生,但无论如何......我不希望我的任务在队列中等待并减慢应用程序的速度)。AppsubmitRunnableTask

在应用程序的回调方法中,我关闭了池。onTerminate

所以我的问题是:这种模式比在代码中创建新的线程更好吗?我的新方法有哪些优点和缺点?它是否会导致我还没有意识到的问题?你能给我一些比这更好的建议来管理我的异步任务吗?

附言:我在Android和Java方面有一些经验,但我远不是一个并发大师)所以在这类问题上可能有一些我不太理解的方面。任何建议将不胜感激。


答案 1

这个答案假设你的任务很短

这种模式是否比在代码中创建新的线程更好?

它更好,但它仍然远非理想。您仍在为短任务创建线程。相反,您只需要创建不同类型的线程池 - 例如通过。Executors.newScheduledThreadPool(int corePoolSize)

行为有什么区别?

  • A 将始终有一组要使用的线程,如果所有线程都忙,则会将新任务放入队列中。FixedThreadPool
  • 由该类创建的 A(默认值)具有它保留的最小线程池,即使在空闲时也是如此。如果新任务传入时所有线程都处于繁忙状态,则会为其创建一个新线程,并在完成后 60 秒释放该线程,除非再次需要它。ScheduledThreadPoolExecutors

第二个可以让你不自己创建新线程。这种行为可以在没有“计划”部分的情况下实现,但您必须自己构建执行程序。构造函数是

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue)

各种选项允许您微调行为。

如果某些任务很长...

我的意思是很长。就像在应用程序生命周期的大部分时间里一样(实时双向连接?服务器端口?组播侦听器?在这种情况下,把你的执行器放在一个执行器中是有害的 - 标准执行程序不是为应对它而设计的,他们的表现会恶化。Runnable

想想你的固定线程池 - 如果你有5个长时间运行的任务,那么任何新任务都会生成一个新线程,完全破坏池的任何可能的收益。如果您使用更灵活的执行器 - 某些线程将被共享,但并非总是如此。

经验法则是

  • 如果是短任务 - 请使用执行器。
  • 如果这是一个很长的任务 - 确保你的执行器可以处理它(即它要么没有最大池大小,要么没有足够的最大线程来处理另外1个线程消失一段时间)
  • 如果它是一个并行进程,需要始终与主线程一起运行 - 请使用另一个线程。

答案 2

要回答您的问题 — 是的,使用 Executor 比创建新线程更好,因为:

  1. 执行程序提供了一系列不同的线程池。它允许重用已经存在的线程,这提高了性能,因为线程创建是一项昂贵的操作。
  2. 如果线程死亡,Executor 可以用新线程替换它,而不会影响应用程序。
  3. 对多线程策略的更改要容易得多,因为只需要更改执行器实现。

推荐