Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 931
Andranik Avatar asked Nov 05 '14 17:11

Andranik


1 Answers

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.
like image 129
Ordous Avatar answered Oct 02 '22 06:10

Ordous