java Concurrent Principle Practice (13) -- thread pool and Executors

Keywords: Java Programming

Thread pool

1, Introduction to thread pool

1. Concept of thread pool:

Thread pool is to create some threads first, and their collection is called thread pool. Using the thread pool can improve the performance very well. When the system starts, the thread pool creates a large number of idle threads. The program passes a task to the thread pool, and the thread pool starts a thread to execute the task. After execution, the thread does not die, but returns to the thread pool again to become idle, waiting for the next task to be executed.

2. Working mechanism of thread pool

In the programming mode of online process pool, the task is submitted to the whole thread pool, rather than directly to a thread. After getting the task, the thread pool will look for whether there are idle threads inside. If there are, the task will be handed over to a free thread.

A thread can only execute one task at a time, but can submit multiple tasks to a thread pool at the same time.

3. Reasons for using thread pool:

Multithreading running time, the system constantly starts and closes new threads, the cost is very high, it will consume system resources, and the danger of transition thread switching, which may lead to the collapse of system resources. At this point, thread pool is the best choice.

2, 5 common thread pools

1. Introduction to the return value ExecutorService of thread pool:

ExecutorService is an interface provided by Java for managing thread pools. Two functions of the interface: controlling the number of threads and reusing threads

2. Five common thread pools are implemented as follows: (the return value is ExecutorService)

①Executors.newCacheThreadPool():

To cache the thread pool, first check whether there are any previously established threads in the pool. If so, use it directly. If not, create a new thread to join the pool, which is usually used to perform asynchronous tasks with short lifetime

Example code:

 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
 public class ThreadPoolExecutorTest {
     public static void main(String[] args) {
         //Create a cacheable thread pool
         ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
         for (int i = 0; i < 10; i++) {
             try {
                 //sleep obviously uses the previous threads in the thread pool, but does not create a new thread
                 Thread.sleep(1000);
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
             cachedThreadPool.execute(new Runnable() {
                 public void run() {
                     //Print cache thread information in progress
                     System.out.println(Thread.currentThread().getName()+"Being executed");
                 }
             });
         }
     }
 }

Output results:

pool-1-thread-1 is being executed
pool-1-thread-1 is being executed
pool-1-thread-1 is being executed
pool-1-thread-1 is being executed
pool-1-thread-1 is being executed
pool-1-thread-1 is being executed
pool-1-thread-1 is being executed
pool-1-thread-1 is being executed
pool-1-thread-1 is being executed
pool-1-thread-1 is being executed

The thread pool is infinite. When the current task is executed, the previous task has been completed. Instead of creating a new thread every time, the threads executing the previous task will be reused

② Executors.newFixedThreadPool(int n):

Create a reusable fixed number of thread pools to run these threads in a shared, unbounded queue.

Example code:

 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
 public class ThreadPoolExecutorTest {
     public static void main(String[] args) {
         //Create a reusable fixed number of thread pool
         ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
         for (int i = 0; i < 10; i++) {
             fixedThreadPool.execute(new Runnable() {
                 public void run() {
                     try {
                         //Print cache thread information in progress
                         System.out.println(Thread.currentThread().getName()+"Being executed");
                         Thread.sleep(2000);
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }
                 }
             });
         }
     }
 }

Output results:

pool-1-thread-1 is being executed
pool-1-thread-2 is being executed
pool-1-thread-3 is being executed
pool-1-thread-1 is being executed
pool-1-thread-2 is being executed
pool-1-thread-3 is being executed
pool-1-thread-1 is being executed
pool-1-thread-2 is being executed
pool-1-thread-3 is being executed
pool-1-thread-1 is being executed

Because the thread pool size is 3, each task prints three results every two seconds after printing the results.
It is better to set the size of the fixed length route pool according to the system resources. Such as Runtime.getRuntime().availableProcessors()

③Executors.newScheduledThreadPool(int n)

Create a fixed length thread pool to support scheduled and periodic task execution

Delay execution sample code:

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ThreadPoolExecutorTest {
    public static void main(String[] args) {
        //Create a fixed length thread pool to support scheduled and periodic task execution -- delayed execution
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
        //Delay execution by 1 second
        scheduledThreadPool.schedule(new Runnable() {
            public void run() {
                System.out.println("Delay execution by 1 second");
            }
        }, 1, TimeUnit.SECONDS);
    }
}

Output result: delay execution for 1 second

Execute sample code periodically:

 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 
 public class ThreadPoolExecutorTest {
     public static void main(String[] args) {
         //Create a fixed length thread pool to support scheduled and periodic task execution - regular execution
         ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
         //Every 3 seconds after 1 second delay
         scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
             public void run() {
                 System.out.println("Every 3 seconds after 1 second delay");
             }
         }, 1, 3, TimeUnit.SECONDS);
     }
 }

Output results:

Every 3 seconds after 1 second delay
Every 3 seconds after 1 second delay
...

④ Executors.newSingleThreadExecutor():

Create a single threaded pool, which only uses unique worker threads to execute tasks, ensuring that all tasks are executed in the specified order (FIFO, LIFO, priority).

Example code:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestThreadPoolExecutor {
    public static void main(String[] args) {
        //Create a single threaded pool
        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            final int index = i;
            singleThreadExecutor.execute(new Runnable() {
                public void run() {
                    try {
                        //The results are output in sequence, which is equivalent to executing tasks in sequence
                        System.out.println(Thread.currentThread().getName()+"Being executed,The printed value is:"+index);
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}

pool-1-thread-1 is being executed and the printed value is: 0
pool-1-thread-1 is being executed and the printed value is: 1
pool-1-thread-1 is being executed and the printed value is: 2
pool-1-thread-1 is being executed and the printed value is: 3
pool-1-thread-1 is being executed and the printed value is: 4
pool-1-thread-1 is being executed and the printed value is: 5
pool-1-thread-1 is being executed and the printed value is: 6
pool-1-thread-1 is being executed and the printed value is: 7
pool-1-thread-1 is being executed and the printed value is: 8
pool-1-thread-1 is being executed and the printed value is: 9

⑤ Executors.newWorkStealingPool():

What's new in jdk1.8: each thread has tasks in the queue to be processed. If a thread completes tasks in its own queue,

Then it can go to other threads to get the tasks of other threads to execute

import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
 
/**
 * Thread pool
 *    1.Fixed number of thread pools
 *    2.Cache thread pool, start threads 0
 *         If a thread is required and the current thread pool does not exist, create the thread pool
 *         If a thread is needed, and there are threads in the thread pool that are not used, use the existing threads
 *         If a thread in the thread pool is not used for more than 60 seconds (the default), the thread stops
 *    3.Thread pool with only 1 thread
 *         Ensure the sequence of thread execution
 *    4.ScheduledPool
 *          Similar to DelayedQueue, scheduled execution
 *    5.WorkStealingPool(Task stealing, all guard threads)
 *          Each thread has tasks in the queue to be processed. If a thread completes tasks in its own queue,
 *          Then it can go to other threads to get the tasks of other threads to execute
 */
public class Demo {
    /*
        4
        1000:ForkJoinPool-1-worker-1
        1000:ForkJoinPool-1-worker-0
        2000:ForkJoinPool-1-worker-2
        3000:ForkJoinPool-1-worker-3
        2000:ForkJoinPool-1-worker-1
        public static ExecutorService newWorkStealingPool() {
        return new ForkJoinPool
            (Runtime.getRuntime().availableProcessors(),
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }
     */
    public static void main(String[] args) throws IOException {
        // Start several threads according to the cpu core
        ExecutorService service = Executors.newWorkStealingPool();
        // View how many cores the current computer is
        System.out.println(Runtime.getRuntime().availableProcessors());
        service.execute(new R(1000));
        service.execute(new R(2000));
        service.execute(new R(3000));
        service.execute(new R(1000));
        service.execute(new R(2000));
 
        // Worksteading is a sprite thread (Guardian thread, background thread). The main thread is not blocked and the output is not visible.
        // Virtual machine does not stop, and daemons do not stop
        System.in.read();
    }
 
    static class R implements Runnable {
        int time;
 
        public R(int time) {
            this.time = time;
        }
 
        @Override
        public void run() {
            try {
                TimeUnit.MILLISECONDS.sleep(time);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(time + ":" + Thread.currentThread().getName());
        }
    }
}

Original link: https://blog.csdn.net/mediocre117/article/details/73374876

3, Buffering queues BlockingQueue and custom thread pool ThreadPoolExecutor

1. Introduction to BlockingQueue:

BlockingQueue is a double buffered queue. Two queues are used inside BlockingQueue, allowing two threads to store and take out one from the queue at the same time. At the same time, it improves the efficiency of queue access.

2. Several commonly used blockingqueues:

  • ArrayBlockingQueue (int i): a BlockingQueue of specified size whose construction must specify the size. The objects it contains are sorted in FIFO order.
  • LinkedBlockingQueue() or (int i): a BlockingQueue with an unfixed size. If it is constructed with a specified size, the generated BlockingQueue has a size limit. If no size is specified, its size is determined by Integer.MAX_VALUE. The objects it contains are sorted in FIFO order.
  • PriorityBlockingQueue() or (int i): similar to LinkedBlockingQueue, but the order of the objects it contains is not FIFO, but depends on the natural order of the objects or the Comparator of the constructor.
  • SynchronizedQueue(): a special BlockingQueue whose operation must be done alternately by putting and taking.

3. Custom thread pool (ThreadPoolExecutor and BlockingQueue are used together):

Custom thread pool, which can be created with ThreadPoolExecutor class, has multiple construction methods to create thread pool.

Common constructors: ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue)

Example code:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

class TempThread implements Runnable {

    @Override
    public void run() {
        // Print cache thread information in progress
        System.out.println(Thread.currentThread().getName() + "Being executed");
        try {
            // sleep ensures that three tasks are executed on three threads in one second
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

public class TestThreadPoolExecutor {
    public static void main(String[] args) {
        // Create an array type buffer waiting queue
        BlockingQueue<Runnable> bq = new ArrayBlockingQueue<Runnable>(10);
        // ThreadPoolExecutor: create a custom thread pool. The number of threads saved in the pool is 3, and the maximum number of threads allowed is 6
        ThreadPoolExecutor tpe = new ThreadPoolExecutor(3, 6, 50, TimeUnit.MILLISECONDS, bq);

        // Create 3 tasks
        Runnable t1 = new TempThread();
        Runnable t2 = new TempThread();
        Runnable t3 = new TempThread();
        // Runnable t4 = new TempThread();
        // Runnable t5 = new TempThread();
        // Runnable t6 = new TempThread();

        // Three tasks are executed on three threads respectively
        tpe.execute(t1);
        tpe.execute(t2);
        tpe.execute(t3);
        // tpe.execute(t4);
        // tpe.execute(t5);
        // tpe.execute(t6);

        // Close custom thread pool
        tpe.shutdown();
    }
}

Output results:

pool-1-thread-1 is being executed
pool-1-thread-2 is being executed
pool-1-thread-3 is being executed

finish

221 original articles published, praised 23, visited 60000+
Private letter follow

Posted by Blaze(!) on Tue, 04 Feb 2020 04:33:33 -0800