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

14,537

Solution 1

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).

What's the difference in behaviour?

  • A FixedThreadPool will 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 Executors class, has a minimum thread 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.

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 Runnable in an executor is detrimental - standard executors are not designed to cope with it, and their performance will deteriorate.

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.

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.

Solution 2

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

  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.

Solution 3

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

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.

Share:
14,537
Andranik
Author by

Andranik

Updated on June 18, 2022

Comments

  • Andranik
    Andranik almost 2 years

    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.

    So now I have created a ThreadPoolExecutor in my Application class which holds 5 threads. Here is the code:

    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:

    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 App and call the submitRunnableTask method 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).

    In the onTerminate callback method of Application I shutdown the pool.

    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.