Open In App

How to Use ThreadPoolExecutor in Android?

Improve
Improve
Like Article
Like
Save
Share
Report

A task queue contains jobs that are waiting to be performed by any of the pool’s idle threads. Producers contribute tasks to the queue, while worker threads function as consumers, consuming jobs from the queue whenever an idle thread is ready to conduct a new background operation.

But before we dive deep, what is exactly a Thread Pool in Android?

It is a thread pooler which handles all the tasks associated with threads and involving them, although the proper number of threads is proportional to how the thread pooler is implemented.

ThreadPoolExecutor

The ThreadPoolExecutor uses one of its threads from the thread pool to complete a particular job.

Java




ThreadPoolExecutor gfgPoolExecutor
    = new ThreadPoolExecutor(
        int mainPoolSize,
        long keepAliveTime,
          int maximumPoolSize,
        TimeUnit unit,
        BlockingQueue<Runnable> workQueue
    );


You might wonder what all these values refer to right? 

  • mainPoolSize: The number of threads to maintain in the pool at all times. There are no threads in the pool at first. However, new threads are generated when jobs are added to the queue. If there are idle threads but the thread count is less than the mainPoolSize, new threads will continue to be generated.
  • keepAliveTime: When the number of threads exceeds the number of core threads, the noncore threads (extra idle threads) will wait for a new job and will terminate if they do not get one within the period indicated by this parameter.
  • maximumPoolSize: The maximum number of threads that can be in the pool at the same time. If this exceeds the mainPoolSize, and the current number of threads is more than the mainPoolSize, additional worker threads will be generated only if the queue is empty.
  • unit: The time unit for keepAliveTime.
  • workQueue: The task queue, which only holds jobs that can be executed. It’s going to have to be a BlockingQueue.

The question arises,  

Why Would you Utilize Thread Pool Executor in an Android or Java app?

It is a strong task execution framework since it allows task queue insertion, task cancellation, and task prioritizing. It lowers the overhead associated with thread creation by managing a predetermined number of threads in its thread pool.

Geek Tip: The number of threads available for different thread pools may vary depending on your needs.

How to Use the Thread Pooler in Android

Step #1: Creating a GfGThreadMechanism 

Java




public class GfGThreadMechanism implements ThreadFactory {
    private final int gfgPriority;
    public PriorityThreadFactory(int threadPriority) {
        gfgPriority = threadPriority;
    }
    @Override
    public Thread newThread(final Runnable gfgRunner) {
        Runnable wrapperRunnable = new Runnable() {
            @Override
            public void run() {
                try {
                    Process.setThreadPriority(gfgPriority);
                } catch (Throwable t) {
 
                }
                gfgRunner.run();
            }
        };
        return new Thread(wrapperRunnable);
    }
}


Step #2: Creating the Master Thread Executor 

Java




public class MasterThreadExecutor implements Executor {
    private final Handler gfgHandler = new Handler(Looper.getMainLooper());
    @Override
    public void execute(Runnable runnable) {
        gfgHandler.post(runnable);
    }
}


Step #3: Creating a singleton class to Execute 

Java




public class gfgExecutor{
     
    // Number of threads, are being set here.
    public static final int NUMBER_OF_THREADS = Runtime.getRuntime().availableProcessors();
     
    // Setting and Monitoring BG Tasks
    private final ThreadPoolExecutor gfgTasks;
     
    // another executor for handling light feather tasks
    private final ThreadPoolExecutor gfgFeatherTasks;
     
    // the thread pool which manages the heavy tasks
    private final Executor publicExecutor;
     
    // an instance of DefaultExecutorSupplier
    private static DefaultExecutorSupplier gfgInstance;
     
    // returns the instance of DefaultExecutorSupplier
    public static DefaultExecutorSupplier getInstance() {
       if (gfgInstance == null) {
         synchronized(DefaultExecutorSupplier.class){                                                                 
             gfgInstance = new DefaultExecutorSupplier();     
        }
        return gfgInstance;
    }
       
    // Main Magic Goes Here
    private DefaultExecutorSupplier() {
        // setting the thread factory
        ThreadFactory backgroundPriorityThreadFactory = new
                PriorityThreadFactory(Process.THREAD_PRIORITY_BACKGROUND);
         
        // setting the thread pool executor for gfgTasks;
        gfgTasks = new ThreadPoolExecutor(
                NUMBER_OF_THREADS * 2,
                NUMBER_OF_THREADS * 2,
                60L,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(),
                backgroundPriorityThreadFactory
        );
         
        // setting the thread pool executor for gfgFeatherTasks;
        gfgFeatherTasks = new ThreadPoolExecutor(
                NUMBER_OF_THREADS * 2,
                NUMBER_OF_THREADS * 2,
                60L,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(),
                backgroundPriorityThreadFactory
        );
         
        // setting the thread pool executor for gfgExecutor;
        gfgExecutor = new MainThreadExecutor();
    }
       
    // thread pool which returns the BG task
    public ThreadPoolExecutor forBackgroundTasks() {
        return gfgTasks;
    }
       
    // similar to the return above, but for feather tasks
    public ThreadPoolExecutor forLightWeightBackgroundTasks() {
        return gfgFeatherTasks;
    }
       
    // similar to the return above, but for heavy tasks
    public Executor forMainThreadTasks() {
        return mMainThreadExecutor;
    }
}


Use this class in any activity now, you are ready to go! 

How do you prioritize a task?

Assume there are 10 jobs in a queue and the thread pool can only handle four threads. Because the thread pool can only perform four activities at a time, we prioritize new jobs. But suppose we require the last job we put in the queue to be completed first. We’d need to give that job IMMEDIATE priority so that when the thread gets a new task from the queue, it runs it first (since it has the highest priority). To prioritize a job, we must first build a thread pool executor. 

Create a Priority ENUM:

Java




// The different urgency levels
public enum Urgency {
   
    // Lowest urgency level
    LOW,
 
    // Medium urgency level
    MEDIUM,
 
    // Highest urgency level
    HIGH,
 
    // Highest urgency level,
    // even up from the previous
    IMMEDIATE;
   
}


Then, we create a runnable for the urgency: 

Java




public class UrgencyRunnable implements Runnable {
    private final Priority gfgUrgency;
 
    public PriorityRunnable(Priority gfgUrgency) {
        this.gfgUrgency = gfgUrgency;
    }
 
    @Override
    public void run() {
      // your code, or leave blank.
    }
 
    public Priority gfgUrgency() {
        return gfgUrgency;
    }
}


Make a PriorityThreadPoolExecutor by extending ThreadPoolExecutor

PriorityFutureTask must be created, which will implement <ComparablePriorityFutureTask>. 

Java




public class UrgencyExecutor extends ThreadPoolExecutor {
   public PriorityThreadPoolExecutor(int gfgPool, int gfgMax, long gfgTime,
         TimeUnit unit, ThreadFactory threadFactory) {
        super(gfgPool, gfgMax, gfgTime, unit,new PriorityBlockingQueue<Runnable>(), threadFactory);
    }
 
    @Override
    public gfgTask<?> submit(Runnable gfgBackTask) {
        PriorityFutureTask gfgTask = new PriorityFutureTask((PriorityRunnable) task);
        execute(gfgTask);
        return gfgTask;
    }
    private static final class PriorityFutureTask extends FutureTask<PriorityRunnable>
            implements Comparable<gfgTask> {
        private final PriorityRunnable priorityRunnable;
 
        public gfgTask(PriorityRunnable priorityRunnable) {
            super(priorityRunnable, null);
            this.priorityRunnable = priorityRunnable;
        }
         
        @Override
        public int compareTo(gfgTask other) {
            Priority gfg1 = priorityRunnable.getPriority();
            Priority gfg2 = other.priorityRunnable.getPriority();
            return gfg2.ordinal() - gfg1.ordinal();
        }
    }
}


To begin, instead of ThreadPoolExecutor in DefaultExecutorSupplier, use PriorityThreadPoolExecutor as follows: 

Java




public class DefaultExecutorSupplier{
private final PriorityThreadPoolExecutor gfgBackTask;
private DefaultExecutorSupplier() {
        gfgBackTask = new PriorityThreadPoolExecutor(
                NUMBER_OF_THREADS * 4,
                NUMBER_OF_THREADS * 3,
                50L,
                TimeUnit.SECONDS,
                backgroundPriorityThreadFactory
        );
    }
}


 
Here’s an example of how we may make a job LOW priority: 

Java




// Doing at LOW
public void doingAtLowUrgency(){
  DefaultExecutorSupplier.getInstance().forBackgroundTasks()
    .submit(new PriorityRunnable(Priority.LOW) {
    @Override
    public void run() {
    // some back tasks go here
    }
  });
}


Conclusion

A task can be prioritized in this manner. The implementation described above is also applicable to any JAVA application. Hope this article cleared out the air for you!

 



Last Updated : 07 Aug, 2021
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads