Android中的新线程(任务).start()VS ThreadPoolExecutor.submit(任务)

时间:2014-11-05 17:52:51

标签: java android multithreading asynchronous threadpoolexecutor

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

所以现在我在我的ThreadPoolExecutor类中创建了一个Application,它拥有5个线程。 这是代码:

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();
    }
}

因此,当我想在我的代码中运行异步任务时,我得到App的实例,并调用submitRunnableTask方法将runnable传递给它。正如你所看到的,我也检查,如果线程池有自由线程来执行我的任务,如果没有,我创建一个新线程(我不认为这会发生,但无论如何......我不希望我的任务在队列中等待并放慢应用程序的速度。)

在Application的onTerminate回调方法中,我关闭了池。

所以我的问题如下:这种模式是否比在代码中创建新线程更好?我的新方法有哪些优点和缺点?它会引起我不知道的问题吗?你能告诉我一些比这更好的东西来管理我的异步任务吗?

P.S。我在Android和Java方面有一些经验,但我远不是一个并发大师。所以可能有些方面我不太了解这类问题。任何建议将不胜感激。

3 个答案:

答案 0 :(得分:20)

此答案假定您的任务很短

  

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

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

行为的差异是什么?

  • FixedThreadPool将始终具有一组要使用的线程,如果所有线程都忙,则新任务将被放入队列中。
  • ScheduledThreadPool类创建的A(默认)Executors具有最小线程池,即使在空闲时也会保留该线程池。如果新任务进入时所有线程都忙,它会为它创建一个新线程,并在完成后60秒处理该线程,除非再次需要它。

第二个允许您不自己创建新线程。这种行为可以在没有"预定"部分,但你必须自己构建执行程序。

是构造函数
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue)

各种选项可让您微调行为。

如果某些任务很长......

我的意思很长。与大多数应用程序生命周期一样(实时双向连接?服务器端口?多播侦听器?)。在这种情况下,将Runnable放入执行程序是有害的 - 标准执行程序旨在应对它,并且它们的性能会恶化。

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

经验法则是

  • 如果是短任务 - 请使用遗嘱执行人。
  • 如果它是长任务 - 确保您的执行者可以处理它(即它没有最大池大小,或者没有足够的最大线程来处理1个以上线程已经消失了一段时间)
  • 如果它是一个需要始终与主线程一起运行的并行进程 - 请使用另一个线程。

答案 1 :(得分:6)

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

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

答案 2 :(得分:4)

根据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...
    }
}

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