The vast sea of millions, thank you for this second you see here. I hope my series of interview questions can help you! ‘!

May you keep your love and go to the mountains and seas in the coming days!

Three interview questions a day, achievement better self

Today let’s continue talking about thread pools in multiple threads.

1. Do you know thread pools? Why use thread pools?

In object-oriented programming, creating and destroying objects is time consuming because creating an object requires memory resources or more.

In Java, each thread created and destroyed in the JVM consumes resources and time. There are also context switches in threads, which require some overhead, and the number of threads created is not always the better. However, if there are too many threads created, the frequency of context switches increases. Perhaps the benefits of multithreading outweigh the costs of thread switching.

So how do we manage threads?

So we can create a container to cache the number of threads for others to use, without having to create and destroy threads ourselves.

Conclusion:

Thread pool is to create a number of executable threads into a pool (container), when needed to get threads from the pool do not need to create, after the use of threads do not need to destroy but put back into the pool, so as to reduce creation and destruction.

Benefits of using thread pools:

  • It reduces resource consumption, reuses existing threads, and reduces resource consumption for thread creation and destruction.
  • Improved response time, no waiting time for creation and destruction, and once the task arrives, it can be executed through the thread pool.
  • Threads are scarce resources. If they are created indefinitely, they will not only consume system resources, but also reduce system stability. Thread pools can be used for unified allocation, tuning and monitoring.

Good ah! Thread pool is also involved, it seems that usually have to learn ah, so let’s continue

2. How many ways do you know to create a thread pool

The following follows the Executors tool class that provides static factory methods for generating common thread pools.

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

    Let’s look at the underlying methods and implementation:

    Bottom:

    public static ExecutorService newCachedThreadPool(a) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
    Copy the code

    The steps we implemented:

    public class ThreadPoolDemo {
    
        public static void main(String[] args) {
            threadPoolTest();
        }
    
        private static void threadPoolTest(a) {
            // 1. Use the factory class to get the thread pool object
            ExecutorService executorService = Executors.newCachedThreadPool();
    
            // 2. Submit the task
            for (int i = 1; i < 8; i++) {
                executorService.submit(newMyRunnable(i)); }}}// Our task class
    class MyRunnable implements Runnable {
    
        private int id;
        public MyRunnable(int id) {
            this.id = id;
        }
        @Override
        public void run(a) {
            // Prints the name of which thread.
            System.out.println(Thread.currentThread().getName() + "Carried out the mission."+ id); }}Copy the code

    Results can be obtained:

    Executorservice.shutdown (); executorservice.shutdown (); executorservice.shutdown (); Each task will create one more thread out.

    NewCacheThreadPool works like this:

    1. Submit tasks to the thread pool.

    2. Because corePoolSize is 0, no core threads are created and the maximum size of the thread pool is integer.max_value.

    3. An attempt was made to add the task to the SynchronousQueue.

    4. If SynchronousQueue is successfully enqueued, it waits to be pulled and executed by the currently running thread. If there are no idle threads, a non-core thread is created, and the task is pulled from the SynchronousQueue and executed on the current thread.

    5. SynchronousQueue will block if a task is waiting.

    When many short-duration tasks need to be executed, the thread reuse rate of newCacheThreadPool is high, which can significantly improve performance. Also, the thread is recycled after 60 seconds, meaning that newCacheThreadPool doesn’t take up a lot of resources even if no tasks are coming in.

  • NewFixedThreadPool: Creates a thread pool of fixed size. 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.

    Let’s take a look at the bottom layer and the code implementation as well:

    Bottom:

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    Copy the code

    Code implementation process:

    public static void main(String[] args) {
        // threadPoolTest();
        threadPoolTest2();
    }
    
    private static void threadPoolTest2(a) {
        // 1. Use the factory class to get the thread pool object
        ExecutorService executorService = Executors.newFixedThreadPool(3);
    
        // 2. Submit the task
        for (int i = 1; i < 8; i++) {
            executorService.submit(newMyRunnable(i)); }}Copy the code

    Results obtained:

    pool-1-thread-2Carried out the mission2
    pool-1-thread-1Carried out the mission1
    pool-1-thread-3Carried out the mission3
    pool-1-thread-3Carried out the mission6
    pool-1-thread-1Carried out the mission5
    pool-1-thread-2Carried out the mission4
    pool-1-thread-3Carried out the mission7
    Copy the code

    The number of core threads is the same as the total number of threads, which is passed in as nThreads, so only core threads can be created, not non-core threads. Because the default size of LinkedBlockingQueue is integer.max_value, if the core thread is idle, it is handed over to the core thread. If the core thread is not free, the queue waits until the core thread is free.

    Differences from newCacheThreadPool:

    • Since corePoolSize == maximumPoolSize, FixedThreadPool only creates core threads. CachedThreadPool creates only non-core threads because corePoolSize=0.
    • In the getTask() method, if there are no tasks available on the queue, the thread will remain blocked in linkedBlockingQueue.take () and the thread will not be reclaimed. The CachedThreadPool will be recovered after 60 seconds.
    • Since threads are not recycled and are stuck in blocks, FixedThreadPool takes up more resources without a task.
    • Both rarely trigger rejection policies, but the principle is different. FixedThreadPool because blocking queues can be so large (up to the Integer maximum) that rejection policies are rarely triggered; CachedThreadPool rarely triggers rejection policies because the thread pool is so large (Integer maximum) that it rarely causes the number of threads to exceed the maximum number of threads.

  • NewSingleThreadExecutor: Creates a single 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

    Let’s take a look at the bottom layer and the code implementation as well:

    Bottom:

    public static ExecutorService newSingleThreadExecutor(a) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1.1.0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
    Copy the code

    Code implementation process:

    public static void main(String[] args) {
        // threadPoolTest();
        // threadPoolTest2();
        threadPoolTest3();
    }
    
    private static void threadPoolTest3(a) {
        // 1. Use the factory class to get the thread pool object
        ExecutorService executorService = Executors.newSingleThreadExecutor();
    
        // 2. Submit the task
        for (int i = 1; i < 8; i++) {
            executorService.submit(newMyRunnable(i)); }}Copy the code

    Results obtained:

    pool-1-thread-1Carried out the mission1
    pool-1-thread-1Carried out the mission2
    pool-1-thread-1Carried out the mission3
    pool-1-thread-1Carried out the mission4
    pool-1-thread-1Carried out the mission5
    pool-1-thread-1Carried out the mission6
    pool-1-thread-1Carried out the mission7
    Copy the code

    As you can see, only one thread is created, and there is only one core thread (corePoolSize == maximumPoolSize=1), using LinkedBlockingQueue (large), so no non-core threads are created. All tasks are executed in a first-come, first-executed order. If the unique thread is not idle, the new task is stored in the task queue waiting to execute.

  • NewScheduledThreadpool: Creates a thread pool of unlimited size. This thread pool supports the need to execute tasks regularly and periodically.

    Let’s take a look at the bottom layer and the code implementation as well:

    Bottom:

    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
    
    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }
    Copy the code

    Code implementation:

    public static void main(String[] args) {
        // threadPoolTest();
        // threadPoolTest2();
        // threadPoolTest3();
        threadPoolTest4();
    }
    
    private static void threadPoolTest4(a) {
        // 1. Use the factory class to get the thread pool object
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
    
        // 2. Delay execution of each task by two seconds
        for (int i = 1; i < 8; i++) {
            scheduledExecutorService.schedule(new MyRunnable(i), 2, TimeUnit.SECONDS);
        }
    
        System.out.println("See if I did it first!");
    }
    Copy the code

    Results you can see:

    Let's see if I did it first! pool-1-thread-1Carried out the mission1
    pool-1-thread-1Carried out the mission4
    pool-1-thread-2Carried out the mission2
    pool-1-thread-3Carried out the mission3
    pool-1-thread-2Carried out the mission6
    pool-1-thread-1Carried out the mission5
    pool-1-thread-3Carried out the mission7
    Copy the code

    We can see that the thread pool creates only the number of threads we specify and returns an interface that inherits from ScheduledExecutorService. It gives us some ways to delay:

    publicScheduledFuture<? > schedule(Runnable command,longdelay, TimeUnit unit); The unit of delay time is unit, the number of time is delay, and the task is a Command of the Runnable type.public <V> ScheduledFuture<V> schedule(Callable<V> callable,
                                            long delay, TimeUnit unit); This method is an overload of the above method, except that the task is of type CallableCopy the code

Oh, that’s a lot of detail. Well, it’s customary to ask you one last question:

3. Do you know the five states of thread pool?

Thread pools have the following five states:

Specific include:

  • RUNNING: This is the most normal state. New tasks can be accepted and tasks in the waiting queue can be processed normally.

  • SHUTDOWN: no new task submissions are accepted, but ongoing business continues to be processed and tasks in the blocking queue are also processed.

  • STOP: does not accept new task submissions, stops processing tasks in the waiting queue, and interrupts the thread executing the task.

  • TIDYING: All tasks are terminated or destroyed, the number of active threads is 0, and the thread pool state terminated by the hook method terminated() when terminated to TIDYING.

  • TERMINATED: The thread pool TERMINATED completely, that is, after the TERMINATED () method TERMINATED, the state of the thread pool will be this.

Nice guy! Today is over here, looking forward to your arrival tomorrow, I hope to continue to keep a surprise!

Resources: Thread pool principles

Note: If there are any mistakes and suggestions, please leave a message! If this article is helpful to you, I hope you will pay attention to it for three times. Thank you very much! You can also search the wechat prince Nezha public number private chat me, thank you guys!