This is the 26th day of my participation in the August Text Challenge.More challenges in August

1. Thread pool Overview

In Android development, we often put a time-consuming task on a single thread to avoid ANR exceptions. But if we have a lot of threads on a page, and the threads end up executing in a short time, we are creating threads so frequently that the system becomes inefficient. So we have thread pools. What does a thread pool do? Thread pool can automatically or manually configure the number of threads in a thread pool according to the system environment variables, so that the creation and recycling of threads reach an ideal state, reduce the consumption of system resources, improve the efficiency of the system. There are several benefits to using thread pools:

Reduce resource consumption. Reduce the consumption caused by thread creation and destruction by reusing the created threads and improve the system endurance by thread pool management. (Each thread requires 1MB of memory). Improve response speed. When a task arrives, it can be executed immediately without waiting for the thread to be created

2. Basic use of thread pools

In jdk1.5, Java provides us with the java.util.concurrent package, which contains the interfaces and classes used for our thread pool. The top-level interface of the thread pool is the Executor interface. It has a execute(Runnable) method to execute the submitted Runnable task. It’s just an interface, so it needs to have an implementation body:

Configuring a thread pool is a complicated task. If we do not know the principle of the thread pool well, it is easy to make the configured thread pool ineffective. So the System provides Executors class, which provides static methods to create common thread pools.

  • Public static ExecutorService newFixedThreadPool(int nThreads) : Creates a thread pool with a fixed number of threads. A thread is created each time a task is submitted until the thread reaches the maximum size of the thread pool. The size of the thread pool stays the same once it reaches its maximum size, and if a thread terminates due to execution exceptions, a new thread is added to the pool.
  • Public static ExecutorService newCachedThreadPool() : Create a cacheable thread pool. If the size of the thread pool exceeds the number of threads needed to process the task, the pool can reclaim some of the idle (60-second non-executing) threads and intelligently add new threads to process the task as the number of tasks increases. This thread pool has no limit on the thread pool size, which is entirely dependent on the maximum thread size that the operating system (or JVM) can create.
  • Public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) : Creates a thread pool that supports timed and periodic task execution. It can be used instead of the Timer class in most cases.
  • Public static ExecutorService newSingleThreadExecutor() : Creates a single-threaded thread pool. This thread pool has only one thread working, which is equivalent to a single thread executing all tasks in serial. If the unique thread terminates due to an exception, a new thread will replace it. This thread pool ensures that all tasks are executed in the order in which they were submitted.

Test cases:

We create a Text thread to test:

    class Text implements Runnable{
        String name;
        public Text(String name){
            this.name = name;
        }

        @Override
        public void run(a) {
            System.out.println(Thread.currentThread().getName() + "--"+ name); }}Copy the code

Now let’s test the thread pool:

ExecutorService newFixedThreadPool(int nThreads) Specifies the number of thread pools that can be created.

   public static void main(String[]args){
        // create a thread pool with a specified size of 3
        ExecutorService exServiceFixed = Executors.newFixedThreadPool(3);
        Text t1 = new Text("t1");
        Text t2 = new Text("t2");
        Text t3 = new Text("t3");
        Text t4 = new Text("t4");
        Text t5 = new Text("t5");
        exServiceFixed.execute(t1);
        exServiceFixed.execute(t2);
        exServiceFixed.execute(t3);
        exServiceFixed.execute(t4);
        exServiceFixed.execute(t5);
        exServiceFixed.shutdown();
    }
Copy the code

Results:

pool-1-thread-1--t1
pool-1-thread-1--t4
pool-1-thread-1--t5
pool-1-thread-2--t2
pool-1-thread-3--t3
Copy the code
We can see that although we performed five thread tasks with the thread pool, we only had the specified three threads working inside the thread pool, thus achieving an efficiency effect. ExecutorService newCachedThreadPool() creates a thread pool with no specified size. Java public static void main(String[]args){// We create a thread pool that can be used for reuse ExecutorService exServiceCached = Executors.newCachedThreadPool(); Text t1 = new Text("t1"); Text t2 = new Text("t2"); Text t3 = new Text("t3"); Text t4 = new Text("t4"); Text t5 = new Text("t5"); exServiceCached.execute(t1); exServiceCached.execute(t2); exServiceCached.execute(t3); exServiceCached.execute(t4); exServiceCached.execute(t5); exServiceCached.shutdown(); }Copy the code

Running results:

    pool-1-thread-1--t1
    pool-1-thread-2--t2
    pool-1-thread-3--t3
    pool-1-thread-4--t4
    pool-1-thread-5--t5
Copy the code

There are five threads in the thread pool, which is reusable, unlimited in size, and as many as the JVM can create on the system.

ScheduledExecutorService newScheduledThreadPool(int corePoolSize) Creates a timed thread pool. Such as:

    public static void main(String[]args){
        // We create a reusable thread pool
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
        Text t1 = new Text("t1");
        Text t2 = new Text("t2");
        Text t3 = new Text("t3");
        Text t4 = new Text("t4");
        Text t5 = new Text("t5");
        scheduledExecutorService.schedule(t1, 2000, TimeUnit.MILLISECONDS);
        scheduledExecutorService.schedule(t2, 2000, TimeUnit.MILLISECONDS);
    }
Copy the code

Note that when using ScheduledExecutorService, it is the realization of the ExecutorService interface interface. Schedule method, which is used to schedule tasks that the thread will execute after the specified time.

In addition, it has regular, continuous tasks,

    scheduledExecutorService.scheduleAtFixedRate(t1, 1000.2000, TimeUnit.MILLISECONDS);
    scheduledExecutorService.scheduleAtFixedRate(t2, 1000.2000, TimeUnit.MILLISECONDS);
    scheduledExecutorService.scheduleWithFixedDelay(t1, 1000.2000, TimeUnit.MILLISECONDS);
Copy the code

Go on a mission every once in a while.

    pool-1-thread-2--t1
    pool-1-thread-1--t2
    pool-1-thread-2--t1
    pool-1-thread-1--t2
    pool-1-thread-2--t1
    pool-1-thread-1--t2
    pool-1-thread-2--t1
    pool-1-thread-1--t2
    pool-1-thread-2--t1
    pool-1-thread-2--t2
    pool-1-thread-2--t1
    pool-1-thread-1--t2
    pool-1-thread-2--t1
    pool-1-thread-1--t2
    pool-1-thread-2--t1
    pool-1-thread-1--t2
    pool-1-thread-2--t1
    pool-1-thread-1--t2
Copy the code

ExecutorService newSingleThreadExecutor creates a single-thread pool

    public static void main(String[]args){
        // We create a reusable thread pool
        ExecutorService executorServiceSingle = Executors.newSingleThreadExecutor();
        Text t1 = new Text("t1");
        Text t2 = new Text("t2");
        Text t3 = new Text("t3");
        executorServiceSingle.execute(t1);
        executorServiceSingle.execute(t2);
        executorServiceSingle.execute(t3);
    }
Copy the code

Results:

pool-1-thread-1--t1
pool-1-thread-1--t2
pool-1-thread-1--t3
Copy the code

We can see that there is only one thread executing our task.

3. Supplementary knowledge of thread pools

An additional point is the executorService.submit () method. This method has three overloads:

Future submit(Callable task);
Future submit(Runnable task, T result);
Future
    public interface Callable<V> {
        /**
         * Computes a result, or throws an exception if unable to do so.
         *
         * @return computed result
         * @throws Exception if unable to compute a result
         */
        V call(a) throws Exception;
    }
Copy the code

Nothing is implemented, just a call() method that, according to the comments, is used to evaluate a result.

Future source code structure:

    public interface Future<V> {
        boolean cancel(boolean mayInterruptIfRunning);
        boolean isCancelled(a);
        boolean isDone(a);
        V get(a) throws InterruptedException, ExecutionException;
        V get(long timeout, TimeUnit unit)
            throws InterruptedException, ExecutionException, TimeoutException;
    }
Copy the code

There are five methods declared in the Future interface, and the purpose of each method is explained below:

  • The cancel method is used to cancel a task, returning true on success or false on failure. The mayInterruptIfRunning parameter indicates whether ongoing tasks that have not completed are allowed to be canceled, and true indicates that ongoing tasks can be canceled. Whether mayInterruptIfRunning is true or false, this method must return false if the task is completed, or if you cancel the completed task. True if the task is executing and false if mayInterruptIfRunning is set to true. If the task has not yet been executed, true is always returned regardless of whether mayInterruptIfRunning is true or false.
  • The isCancelled method indicates whether the task was cancelled successfully or returns true if the task was cancelled before it completed normally.
  • The isDone method indicates whether the task is complete, and returns true if it is.
  • The get() method is used to get the result of the execution, which blocks and does not return until the task is finished.
  • Get (long timeout, TimeUnit Unit) is used to obtain the execution result. If no result is obtained within the specified time, null is returned.

In other words, Future provides three functions:

1) Judge whether the task is completed;

2) Can interrupt the task;

3) Can obtain the task execution results.

Because a Future is just an interface, it cannot be used directly to create objects, hence the following FutureTask.

There are several methods to determine whether a task is over, and the get method to get the result of the task. Let’s just look at an example.

    public class MainDemo {
        public static void main(String[]args){
            // We create a reusable thread pool
            ExecutorService executorServiceSingle = Executors.newSingleThreadExecutor();
            Future<Integer> future = executorServiceSingle.submit(new AddCallable());
            try {
                System.out.println(future.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch(ExecutionException e) { e.printStackTrace(); }}}class AddCallable implements Callable<Integer>{

        @Override
        public Integer call(a) throws Exception {
            // Execute our business logic processing
            return 2+3; }}Copy the code

We implement a Callable interface to process our task and then retrieve the result of the task through the Future. If the Executor background thread pool has not completed the calculation of the Callable, this calls the get() method that returns the Future object and blocks until the calculation is complete. This is why you need to catch InterruptedException. Is this similar to our Android download task? Start a download task and process it in the UIThread sent by Handler.

There is also a combination of Callable+FutureTask

    Task task = new Task();
    FutureTask<Integer> futureTask = new FutureTask<Integer>(task);
    Thread thread = new Thread(futureTask);
    thread.start();
Copy the code

4. To summarize

In line with the principle of reviewing knowledge points, and did not introduce the configuration of thread pool, you can go to search relevant materials to learn, review this knowledge point, is preparing to write a simple framework for the use of Android network request in these days, so knowledge points also briefly introduced the usage of knowledge, specific in-depth analysis did not do.