07 in-depth understanding of the Java thread pool

Source: Internet
Author: User
Tags volatile

Before the interview Baba encounter a relatively simple multithreaded programming problem, that is, "3 Threads Output ADC", the answer is not very good, deeply guilty, decided to learn more carefully "the art of concurrent programming," a book to reach the strength of mastery. (two months before the rest of the time spent in the LOL and eat chicken, it is a good time, recommended that everyone code to write tired can play under the chicken, is a nice adjustment)

Process Analysis

Java's thread pool is the most commonly used concurrency framework, and reasonable use of thread pool can reduce system consumption, improve response speed, and improve the manageability of threads. The underlying processing flow for the thread pool is as shown.

4 of the winning red is exactly the core of the build thread pool, core thread pool size, queue, thread pool size, reject policy 4 parts. Usually, according to the current system's number of CPUs to set the thread size, and an application of a thread pool, easy to manage, too many threads because of the reason for the switch, but can not improve the utilization of system resources, the following is the personal common thread pool management class, but now AtomicReference the usage is not a special understanding.

/** * 单个应用共用一个线程池,便于管理 */public class ApplicationExecutorFactory {    private static AtomicReference<ExecutorService> serviceRef = new AtomicReference<>(            Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2));    public static ExecutorService getExecutor() {        return serviceRef.get();    }    public static void shutdown() {        if (serviceRef.get() == null)            return;        synchronized (ApplicationExecutorFactory.class) {            if (serviceRef.get() == null)                return;            serviceRef.get().shutdown();            serviceRef.set(null);        }    }}

Next take a look at the most common construction methods for fixed-size threads, and newFixedThreadPool() you can see that the core thread pool size and the maximum size of the thread pools are consistent in the following code, and the queue uses a blocking queue in the form of a list (the default size is int max), the thread factory DefaultThreadFactory and the Reject processor AbortPolicy The default is also used.

//按照层次剖析,涉及类Executors, ThreadPoolExecutor, LinkedBlockingQueuepublic static ExecutorService newFixedThreadPool(int nThreads) {    return new ThreadPoolExecutor(nThreads, nThreads,                                  0L, TimeUnit.MILLISECONDS,                                  new LinkedBlockingQueue<Runnable>());}public ThreadPoolExecutor(int corePoolSize,                          int maximumPoolSize,                          long keepAliveTime,                          TimeUnit unit,                          BlockingQueue<Runnable> workQueue) {    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,         Executors.defaultThreadFactory(), defaultHandler);}public static ThreadFactory defaultThreadFactory() {    return new DefaultThreadFactory();}private static final RejectedExecutionHandler defaultHandler =    new AbortPolicy();public LinkedBlockingQueue() {    this(Integer.MAX_VALUE);}

TIP:
This introduces several questions to strengthen learning.
A. What are the types of blocking queues (also called task Queues)? is the queue size integer.max_value appropriate?? What is a bounded queue and what is a unbounded queue? Does unbounded queuing causemaximuxPoolSizeFailure?
Arrayblockingqueue: Bounded blocking queue based on array structure, followed by FIFO
Linkedblockingqueue: Linked list-based blocking queues, where throughput is generally higher than arrayblockingqueue, and a fixed-size thread pool is the queue that is used.
Synchronousqueue: A blocking queue that does not store elements (interesting), each insert operation must wait until another thread calls the remove operation, or the insert operation is blocked. Swallowing amount Ibanez higher than linkedblockingqueue,cachedthreadpool used the queue,not very well understood at the moment?
Priorityblockingqueue: An infinite-blocking queue with a priority.
B. What if you want to name thread pool threads?
Using the GuavaThreadFactoryBuilderYou can quickly set meaningful names to thread constructor threads.
c. Thread pool In addition to fixed-size, what types, according to the scene how to choose? (The question will be answered in the executor section)
D. What are the Deny policies (also called saturation policies)?
AbortPolicy: Throws an exception directly
Callerrunspolicy: How do I understand a task that only runs on the caller's thread?
Discardoldestpolicy: Discards the oldest task in the queue and executes the current task
Discardpolicy: Direct Discard not processing
In addition, it can be implemented according to the scene.RejectedExecutionHandlerinterface to log or persist a task that the store cannot handle.
E.keepAliveTime,TimeUnitWhat is the use of parameters? This parameter determines the time that a worker is idle, and for a scenario with a lot of tasks but a short execution time for a single task, you can increase the utilization time and see that many middleware can be done.

Operating mechanism

Next, learn more about how the thread pool works.

1. If the currently running thread is less than corepoolsize directly creating a new thread to perform the task, need to obtain a global lock .
2. If the thread that is running equals or is redundant corepoolsize the task into Blockingqueue.
3. If the task cannot be added to Blockingqueue because the queue is full, a new thread task is created and a global lock needs to be acquired.
4. If you create a new thread that will manipulate maximumpoolsize, the task is rejected and the Rejectedexecutionhandler.rejectedexecution () method is called.
Threadpoolexecutor takes the steps above to ensure that execute () is executed, as much as possible to avoid acquiring a global lock, and most likely to perform step 2 without acquiring a global lock. is visualized by the following source code.

Old version of JDK, easy to understand public void execute (Runnable command) {if (command = = null) throw new NullPointerException ();/    or a conditional operator, the value of the first half is evaluated first, and if the current number of threads in the thread pool is not less than the core pool size//, then the following if statement block is entered directly. If the current number of threads in the thread pool is less than the core pool size, then execute the second half, that is, execute if (poolsize >= corepoolsize | |!addifundercorepoolsize (command)) {//if the current thread The pool is in the RUNNING state, the task is placed in the task cache queue, if (runstate = = RUNNING && workqueue.offer (command)) {//This sentence is judged to prevent this task from being added                into the task cache queue while other threads suddenly call shutdown//or Shutdownnow method to shut down a thread pool for a contingency if (runstate! = RUNNING | | poolsize = 0)        ensurequeuedtaskhandled (command); }//If the current thread pool is not in the running State or the task has failed to put the cache queue, then the else if (!addifundermaximumpoolsize command) reject (command) is executed ;    is shutdown or saturated}}private boolean addifundercorepoolsize (Runnable firsttask) {Thread t = null; First get to the lock, because this place involves the change of the thread pool state, first determine whether the number of threads in the current thread pool is less than the core pool size by the IF statement,//A friend may have a question: Has the previous execute () method not already been judged? Only the thread pool executes the Addifundercorepoolsize method when the number of front-line threads is less than the core pool size, why should this place continue to judge? The reason is simple, the previous judgment process is not locked, so it may be in the Execute method to judge the time poolsize less than corepoolsize, and after the judgment,//In other threads and then to the thread pool to submit the task,    May cause poolsize not less than corepoolsize, so need to continue to judge in this place.    Final Reentrantlock mainlock = This.mainlock;    Mainlock.lock ();        try {if (Poolsize < corepoolsize && Runstate = = RUNNING) T = addthread (firsttask);    Create thread to execute Firsttask task} finally {Mainlock.unlock ();    } if (t = = null) return false;    T.start (); return true;} In the Addthread method, a Worker object is first created with the submitted task, and then the thread factory threadfactory creates a new thread t,//then assigns a reference to the thread T to the member variable thread of the Worker object. The worker object is then added to the working set by Workers.add (W).    Private Thread Addthread (Runnable firsttask) {worker w = new Worker (firsttask);  Thread t = threadfactory.newthread (w);            Creates a thread that performs the task if (t! = null) {w.thread = t;        Assigns a reference to the created thread to the member variable Workers.add (w) of W;     int NT = ++poolsize;    Current number of threads plus 1 if (NT > largestpoolsize) largestpoolsize = NT; } returnt;}    Private Final class Worker implements Runnable {private final Reentrantlock Runlock = new Reentrantlock ();    Private Runnable Firsttask;    volatile long completedtasks;    Thread thread;    Worker (Runnable firsttask) {this.firsttask = Firsttask;    } Boolean isActive () {return runlock.islocked ();        } void Interruptifidle () {final Reentrantlock runlock = This.runlock;            if (Runlock.trylock ()) {try {if (thread! = Thread.CurrentThread ()) Thread.Interrupt ();            } finally {Runlock.unlock ();    }}} void Interruptnow () {thread.interrupt ();        } private void RunTask (Runnable Task) {final Reentrantlock runlock = This.runlock;        Runlock.lock (); try {if (Runstate < STOP && thread.interrupted () && runstate            >= STOP) Boolean ran = false; BeforeExecute (thread, Task);  BeforeExecute method is a method of the Threadpoolexecutor class, there is no specific implementation, the user can according to//own need to overload this method and the following AfterExecute method to do some statistics, such as the execution time of a task, etc.                try {task.run ();                ran = true;                AfterExecute (task, NULL);            ++completedtasks;                } catch (RuntimeException ex) {if (!ran) AfterExecute (task, ex);            Throw ex;        }} finally {Runlock.unlock ();        }} public void Run () {try {Runnable task = Firsttask; As can be seen from the implementation of the Run method, it first executes the task Firsttask through the constructor, after the call Runtask () executes the firsttask,//In the while loop constantly through the gettask () to fetch new tasks to execute, So where to take it?            Naturally, it is taken from the task cache queue,//gettask is the method in the Threadpoolexecutor class, not the method in the worker class, the following is the implementation of the Gettask method: Firsttask = null; while (task! = NULL | |                (Task = Gettask ()) = null) {runTask (Task);            task = null;   }} finally {Workerdone (this); //Cleanup works when there are no tasks in the task queue}}}runnable Gettask () {for (;;)            {try {int state = Runstate;            if (State > SHUTDOWN) return null;            Runnable R;            if (state = = SHUTDOWN)//help drain queue r = Workqueue.poll (); else if (poolsize > Corepoolsize | | allowcorethreadtimeout)//If the number of threads is greater than the core pool size or allows idle time to be set for the core pool thread,//The task is taken through poll            , the null R = Workqueue.poll (KeepAliveTime, timeunit.nanoseconds) is returned if the task is not taken for a certain amount of time.            else R = Workqueue.take ();            if (r! = null) return r; if (Workercanexit ()) {//If the task is not taken, that is, if R is null, then whether the current worker can exit if (runstate >= SHUTDOWN)//Wake up othe   RS Interruptidleworkers ();            Break the worker in the idle state to return null;        }//Else retry} catch (Interruptedexception IE) {//on interruption, Re-check runstate   } }}//jdk1.8public void Execute (Runnable command) {if (command = = null) throw new NullPointerException ();    int c = Ctl.get ();        if (Workercountof (c) < corepoolsize) {if (Addworker (command, True)) return;    c = Ctl.get ();        } if (IsRunning (c) && workqueue.offer (command)) {int recheck = Ctl.get ();        if (! isrunning (Recheck) && Remove (command)) reject (command);    else if (workercountof (recheck) = = 0) Addworker (null, FALSE); } else if (!addworker (command, False)) reject (command);}

TIP:
This introduces several questions to strengthen learning.
A. how is the global lock here understood?
B. what are the thread pool states
ThreadPoolExecutora volatile variable is defined in, and several static final variables are defined to represent each state of the thread pool (more complex jdk1.5,1.8 does not fit into the description).

volatile int runState;static final int RUNNING    = 0;static final int SHUTDOWN   = 1;static final int STOP       = 2;static final int TERMINATED = 3;

Runstate represents the state of the current thread pool, which is a volatile variable used to guarantee visibility between threads , and the following static final variables represent several possible values for runstate.
When the thread pool is created, it is initially in the running state;
If the shutdown () method is called, then the thread pool is in the shutdown state, and the line pool is not able to accept the new task, and it waits for all tasks to complete;
If the Shutdownnow () method is called, the thread pool is in the stop state, and this time, the thread pools are unable to accept new tasks, and will attempt to terminate the task being performed;
When the thread pool is in the shutdown or stop state, and all worker threads have been destroyed, the task cache queue has been emptied or the execution finishes, the thread pool is set to the terminated state.

Supplemental knowledge and self-made thread pool

Submit Task : execute() , submit() submit a task with a return value
turn off the thread pool: shutdown shutdownNow All of them are traversing the worker threads in the thread pool, calling the method one at a time interrupt , and all the tasks that cannot respond to the interrupt can never be stopped. The former sets the thread pool state to SHUTDOWN , then interrupts the threads that do not perform the task, the latter sets the state to STOP , and then attempts to stop all executing or suspending threads and returns a list of pending tasks. When all tasks are closed, the thread closes successfully, which is the isTerminated return success.
thread pool selection : In general, tasks of different nature can be handled separately by different sizes of thread pools, CPU-intensive tasks should be configured as small as possible, and IO-intensive may be configured as many as possible, and mixed-type tasks are best done by splitting up the task before making a tradeoff.
For IO-intensive operations such as database requests and service requests, you can set the thread pool relatively large, depending on the situation on the line (average time-consuming).
Queue Selection : It is recommended to use a bounded queue to avoid the thread pool draining the resources of the entire system, and of course, it is necessary to provide a good saturation strategy.

Next, share a personal self-made simplified thread pool by analyzing the core principles of thread pooling.

public class Customthreadpool implements Executorservice {/** * thread pool state part */volatile int runstate;    static final int RUNNING = 0;    static final int SHUTDOWN = 1;    static final int STOP = 2;    static final int TERMINATED = 3;    /** * thread pool Basic Parameters section */int coremaxsize;//maximum thread pool size and maximum core thread size simplifies the final blockingqueue<runnable> queue;    Rejectedexecutionhandler handler; The thread pool is not simple to use a list<thread> management, but with the concept of a worker, the worker needs both the threading and the task to work//think about the production line workers worker,task such as producing a glove,    A thread, such as a robotic arm, can choose a free robotic arm for production final hashset<customworker> workers = new hashset<customworker> (); Final Reentrantlock mainlock = new Reentrantlock ();//thread pool Primary state lock, to thread pool state (size, runstate) private volatile threadfactory thre Adfactory;        Thread factory, used to create thread public Customthreadpool () {runstate = RUNNING;        Coremaxsize = 4;        Queue = new Customblockingqueue (100);        Handler = new Customrejectedexecutionhandler (); Threadfactory = new ThreadfactorYbuilder (). Setnameformat (""). Build (); } @Override public void execute (Runnable command) {//1. Determine if the thread pool is full if (getcursize () <= coremaxsize            && runstate = = RUNNING) {createworker (command);        Return }//2. Determine if the queue is full if (getcursize () <= coremaxsize && runstate = = RUNNING) {enqueue (comm            and);        Return    }//3. Determine if the thread pool is full, omit//4. Process rejecthandle (command) according to saturation strategy;        private void Createworker (Runnable command) {customworker worker = new Customworker ();        Thread THD = threadfactory.newthread (command);        Worker.setthread (THD);        Mainlock.lock ();        try {workers.add (worker);        } finally {Mainlock.unlock ();    } worker.run ();    private void Rejecthandle (Runnable command) {//handler.rejectedexecution (command, this); } Private Boolean Enqueue (Runnable command) {return QuEue.add (command);        } private int getcursize () {mainlock.lock ();        try {return workers.size ();        } finally {Mainlock.unlock (); }} @Override public void shutdown () {for (Customworker worker:workers) {worker.interrupt ()        ; }    }}
Interview questions

1. "3 Threads Loop output ADC"?
A. Implementing with Object objects

public class Abcdemo {public static void Test () {Object locka = new Object ();        Object lockb = new Object ();        Object LOCKC = new Object (); Thread ThA = new Thread (new Runnable () {@Override public void run () {for (int i = 0 ; I < 10;                        i++) {try {System.out.println ("a thread acquires C lock");                            Synchronized (LOCKC) {System.out.println ("A thread begins to wait for a C lock signal");                            Lockc.wait ();                        System.out.println ("A thread ends waiting for C lock signal");                                                } System.out.println ("A thread releases the C lock");                                                System.out.println ("A");                        System.out.println ("A thread acquires a lock");                            Synchronized (Locka) {System.out.println ("A thread begins to send a lock signal");                    Locka.notify ();        System.out.println ("A thread ends sending a lock signal");                    } System.out.println ("A thread releases a lock");                    } catch (Interruptedexception e) {e.printstacktrace ();        }                }            }        }); Thread ThB = new Thread (new Runnable () {@Override public void run () {for (int i = 0 ; I < 10;                        i++) {try {System.out.println ("B thread acquires a lock");                            Synchronized (Locka) {System.out.println ("B thread begins to wait for a lock signal");                            Locka.wait ();                        SYSTEM.OUT.PRINTLN ("B thread end waits for a lock signal");                                                } System.out.println ("B thread releases a lock");                                                System.out.println ("B");                        System.out.println ("B-thread acquires B-lock");     Synchronized (LOCKB) {                       System.out.println ("B-thread starts sending B-lock signal");                            Lockb.notify ();                        SYSTEM.OUT.PRINTLN ("B thread end sending B lock signal");                    } System.out.println ("b thread release B lock");                    } catch (Interruptedexception e) {e.printstacktrace ();        }                }            }        }); Thread ThC = new Thread (new Runnable () {@Override public void run () {for (int i = 0 ; I < 10;                        i++) {try {System.out.println ("C Thread acquires B lock");                            Synchronized (lockb) {System.out.println ("C thread begins to wait for B-lock signal");                            Lockb.wait ();                        SYSTEM.OUT.PRINTLN ("C thread end waits for B lock signal");                                                } System.out.println ("C Thread release B lock");         System.out.println ("C");                                       SYSTEM.OUT.PRINTLN ("C Thread acquires C lock");                            Synchronized (LOCKC) {System.out.println ("C thread starts sending C lock signal");                            Lockc.notify ();                        SYSTEM.OUT.PRINTLN ("C thread ends sending C lock signal");                    } System.out.println ("C Thread release C lock");                    } catch (Interruptedexception e) {e.printstacktrace ();        }                }            }        });        SYSTEM.OUT.PRINTLN ("The main thread turns on a threads");        Tha.start ();        System.out.println ("Main thread turns on B threads");        Thb.start ();        SYSTEM.OUT.PRINTLN ("The main thread turns on C threads");                Thc.start ();        System.out.println ("Main thread acquires C lock");            Synchronized (LOCKC) {SYSTEM.OUT.PRINTLN ("The main thread starts sending a C-lock signal");            Lockc.notify ();        SYSTEM.OUT.PRINTLN ("The main thread end sends a C-lock signal");    } System.out.println ("Main thread release C lock"); }}

B. Implementation through Reentrantlock (implemented in the Reentrantlock chapter)

Resources

    1. Fang Fei. The art of Java concurrent programming [M]. Shanghai: Mechanical industry Press, 2017.
    2. Recommended blog--java Concurrent Programming: The use of thread pools

07 in-depth understanding of the Java thread pool

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.