java Android 中的 new Thread(task).start() VS ThreadPoolExecutor.submit(task)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26763835/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 10:33:18  来源:igfitidea点击:

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

javaandroidmultithreadingasynchronousthreadpoolexecutor

提问by Andranik

In my Android project I had a lot of places where I need to run some code asynchronously (a web request, call to db etc.). This is not long running tasks (maximum a few seconds). Until now I was doing this kind of stuff with creating a new thread, passing it a new runnable with the task. But recently I have read an article about threads and concurrency in Java and understood that creating a new Thread for every single task is not a good decision.

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

So now I have created a ThreadPoolExecutorin my Applicationclass which holds 5 threads. Here is the code:

所以现在我ThreadPoolExecutor在我的Application班级中创建了一个包含 5 个线程的线程。这是代码:

public class App extends Application {

    private ThreadPoolExecutor mPool;

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

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

And also I have a method to submit Runnable tasks to the executor:

而且我还有一种方法可以将 Runnable 任务提交给执行程序:

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

So when I want to run an asynchronous task in my code I get the instance of Appand call the submitRunnableTaskmethod passing the runnable to it. As you can see, I also check, if the thread pool has free threads to execute my task, if not, I create a new Thread (I don't think that this will happen, but in any case... I don't want my task to wait in a queue and slow down the app).

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

In the onTerminatecallback method of Application I shutdown the pool.

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

So my question is the following: Is this kind of pattern better then creating new Threads in code? What pros and cons my new approach has? Can it cause problems that I am not aware off yet? Can you advice me something better than this to manage my asynchronous tasks?

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

P.S. I have some experience in Android and Java, but I am far from being a concurrency guru ) So may be there are aspects that I don't understand well in this kind of questions. Any advice will be appreciated.

PS 我在 Android 和 Java 方面有一些经验,但我远不是一个并发专家)所以在这类问题中可能有一些我不太了解的方面。任何建议将被认真考虑。

回答by Ordous

This answer assumes your tasks are short

此答案假定您的任务很短

Is this kind of pattern better then creating new Threads in code?

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

It's better, but it's still far from ideal. You are still creating threads for short tasks. Instead you just need to create a different type of thread pool - for example by Executors.newScheduledThreadPool(int corePoolSize).

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

What's the difference in behaviour?

行为有何不同?

  • A FixedThreadPoolwill always have a set of threads to use and if all threads are busy, a new task will be put into a queue.
  • A (default) ScheduledThreadPool, as created by the Executorsclass, has a minimumthread pool that it keeps, even when idle. If all threads are busy when a new task comes in, it creates a new thread for it, and disposes of the thread 60 seconds after it is done, unless it's needed again.
  • AFixedThreadPool将始终有一组线程要使用,如果所有线程都忙,则将新任务放入队列中。
  • A (default) ScheduledThreadPool,由Executors类创建,有一个最小的线程池,即使在空闲时也是如此。如果新任务进入时所有线程都忙,它会为其创建一个新线程,并在完成后 60 秒处理该线程,除非再次需要它。

The second one can allow you to not create new threads by yourself. This behaviour can be achieved without the "Scheduled" part, but you will then have to construct the executor yourself. The constructor is

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

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

The various options allow you to fine-tune the behaviour.

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

If some tasks are long...

如果有些任务很长...

And I mean long. As in most of your application lifetime (Realtime 2-way connection? Server port? Multicast listener?). In that case, putting your Runnablein an executor is detrimental - standard executors are notdesigned to cope with it, and their performance will deteriorate.

我的意思是长。就像您的应用程序生命周期的大部分时间一样(实时 2 路连接?服务器端口?多播侦听器?)。在这种情况下,将您Runnable的执行程序放在执行程序中是有害的 - 标准执行程序不是为了应对它设计的,它们的性能会下降。

Think about your fixed thread pool - if you have 5 long-running tasks, then any new task will spawn a new thread, completely destroying any possible gains of the pool. If you use a more flexible executor - some threads will be shared, but not always.

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

The rule of thumb is

经验法则是

  • If it's a short task- use an executor.
  • If it's a long task- make sure your executor can handle it (i.e. it either doesn't have a max pool size, or enough max threads to deal with 1 more thread being gone for a while)
  • If it's a parallel process that needs to always run alongside your main thread - use another Thread.
  • 如果这是一项简短的任务- 使用执行程序。
  • 如果这是一项很长的任务- 确保您的执行程序可以处理它(即它没有最大池大小,或者有足够的最大线程来处理 1 个暂时消失的线程)
  • 如果它是一个需要始终与主线程一起运行的并行进程 - 使用另一个线程。

回答by Tanay

To answer your question — Yes, using Executor is better than creating new threads because:

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

  1. Executor provides a selection of different thread pools. It allows re-use of already existing threads which increases performance as thread creation is an expensive operation.
  2. In case a thread dies, Executor can replace it with a new thread without affecting the application.
  3. Changes to multi-threading policies are much easier, as only the Executor implementation needs to be changed.
  1. Executor 提供了一系列不同的线程池。它允许重用已经存在的线程,从而提高性能,因为线程创建是一项昂贵的操作。
  2. 如果线程死亡,Executor 可以用新线程替换它,而不会影响应用程序。
  3. 更改多线程策略要容易得多,因为只需要更改 Executor 实现。

回答by Andranik

Based on the comment of Ordous I have modified my code to work with only one pool.

根据 Ordous 的评论,我修改了我的代码以仅使用一个池。

public class App extends Application {

    private ThreadPoolExecutor mPool;

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

        mPool =  new ThreadPoolExecutor(5, Integer.MAX_VALUE, 1, TimeUnit.MINUTES, new SynchronousQueue<Runnable>());
    }
}


public void submitRunnableTask(Runnable task){
    if(!mPool.isShutdown() && mPool.getActiveCount() != mPool.getMaximumPoolSize()){
        mPool.submit(task);
    } else {
        new Thread(task).start(); // Actually this should never happen, just in case...
    }
}

So, I hope this can be useful to someone else, and if more experienced people have some comments on my approach, I will very appreciate their comments.

所以,我希望这对其他人有用,如果更有经验的人对我的方法有一些评论,我将非常感谢他们的评论。