Java Concurrent Programming Series 25: Thread pool __ Concurrent Programming

Source: Internet
Author: User

Thread Pool Introduction

In the previous article on the executor framework, there is a preliminary understanding of the thread pool, which is actually common in Java, such as the constant pool in the JVM, and the database connection pool that Web development uses. These pools are essentially object pools in Java, because the pools are all Java objects. Back to the thread pool, almost all programs that need to be asynchronous or perform concurrent tasks can be used to the threads pool. The benefits of using a thread pool mainly include the following:

One, improve the utilization of resources. Because the threads in the thread pool make it reusable, they achieve the purpose of recycling
Second, improve the response speed. Because thread creation is also expensive, it can naturally improve responsiveness if the request arrives with a thread object that has already been created.
Third, to facilitate the unified management of threads. Thread is a scarce resource, if unlimited creation, will not only consume a lot of resources will also greatly reduce the stability of the system. Using a thread pool allows for uniform allocation and monitoring of threads.

realization principle of thread pool

In fact, the thread pool can be summed up in one sentence: by storing the thread that was created in advance, you can use it directly when you need it. However, in order to improve the performance of the thread pool, the actual thread pool is much more complex than the simplified version. In the previous thread pool, only runnable and callable tasks, or unit of work, are received. Then analyze what happens when a unit of work is submitted to the thread pool. The thread pool first determines whether the threads of the core thread pool are performing tasks. If not, a new worker thread is created to perform the task. If you are performing a task, i.e. without an idle thread, go to the next process thread pool to continue to determine if the task queue is full. If the work queue is not full, put the newly submitted task into the work queue, and if the work queue is full, go to the next process thread pool to determine if the thread in the thread pool is working. If not, a new thread is created to perform the commit task, and if so, the saturation policy is executed.

The following is the thread pool execution process:

The core class of the thread pool implemented in Java is Threadpoolexecutor, and the execution process of the Execute method of the class is the process above. Note the top three bold words: Core thread pool, work queue, and saturation policy. The process of refining to Threadpoolexecutor executes the Execute method is supplemented by the following procedure: The core thread pool corresponds to the value of the corepoolsize variable, and if the running thread is less than corepoolsize, a new thread execution task is created ( This process requires a global lock ), and if the running thread is greater than corepoolsize, the task is added to the blockingqueue (corresponding work queue), and if you cannot join, create a new thread to perform the task, and in this step, If the number of threads currently running after creating a new thread is greater than maximumpoolsize, the task is rejected and the Rejectedexecutionhandler.rejectedexcution () method is invoked.

Threadpoolexecutor to avoid getting a global lock on a newly committed task, Threadpoolexecutor performs a warm-up process after it is created, which means that the number of threads currently running is greater than or equal to corepoolsize. In this way, the newly submitted tasks will be added directly to the blockingqueue. This process is not required to acquire a global lock, it will naturally improve the performance of the thread pool. In order to execute the Execute method on the Threadpoolexecutor the process of a probe, to grilled its source code:

public void Execute (Runnable command) {if (command = = null) throw new NullPointerException ();
        int c = Ctl.get (); If the number of threads currently running is less than corepoolsize, create a new thread//execute the current task if (Workercountof (c) < corepoolsize) {if (AD
            Dworker (command, True)) return;
        c = Ctl.get (); //If the number of threads currently running is greater than or equal to corepoolsize or thread creation failed//The current task is placed in the work queue if (IsRunning (c) && Workqueue.offer (
            command) {int recheck = Ctl.get (); Determine if the thread has already been added to perform the task (as it may have been before)//created) or if the thread pool has been closed. If//Two of the answers are yes, then select Reject to perform the task if (! isrunning (Recheck) && Remove (command) Rej
            ECT (command);
        else if (workercountof (recheck) = = 0) Addworker (null, FALSE); ///If the thread pool task cannot be added to the work queue (indicating that the work queue is full)//create a thread to perform the task.
       If the number of threads currently running after the new creation is greater than//maximumpoolsize, the task else if (!addworker (command, false) is rejected)     Reject (command); }

If the thread pool can create threads to perform tasks, the Addworker method is invoked, and threads created by the thread pool are encapsulated as Worker,worker, which is also cycled through the tasks in the queue after the task is executed. Look at the source code of the Addworker method:

    Private Boolean Addworker (Runnable Firsttask, Boolean core) {//Omit part of code Boolean workerstarted = false;
        Boolean workeradded = false;
        Worker w = null;
            try {//here will be the submitted task encapsulated into a worker w = new Worker (Firsttask);
            Final Thread t = w.thread;
                if (t!= null) {//Add a worker thread using the Lock method the final reentrantlock mainlock = This.mainlock;
                Mainlock.lock (); try {///check the running state of the thread pool again during the acquisition of the lock: throw an exception int rs = RU if the//thread pool is closed or the task is empty
                    Nstateof (Ctl.get ());
                        if (Rs < SHUTDOWN | | (rs = = SHUTDOWN && firsttask = null))
                        {if (t.isalive ()) throw new Illegalthreadstateexception ();
                        Join the Worker Array Workers.add (W);
                    int s = workers.size ();    if (S > largestpoolsize) largestpoolsize = s;
                    Workeradded = true;
                finally {Mainlock.unlock ();
                    } if (workeradded) {//If the addition succeeds, start the thread to perform the task T.start ();
                Workerstarted = true;
        finally {if (! workerstarted) addworkerfailed (w);
    return workerstarted; }

After we look at what happens after executing t.start (), because the worker itself implements Runnable, so start will invoke the worker's Run method, the source code is as follows:

        public void Run () {
           runworker (this);
       }
       final void Runworker (Worker w) {
        Thread wt = Thread.CurrentThread ();
        Runnable task = W.firsttask;
        W.firsttask = null;
        W.unlock (); Allow Interrupts
        Boolean completedabruptly = true;
        try {while
            task!= null | | (Task = Gettask ())!= null) {
                w.lock ();
                try {
                    beforeexecute (WT, task);
                    Throwable thrown = null;
                    task.run ();
                    AfterExecute (task, thrown);
                finally {
                    task = null;
                    w.completedtasks++;
                    W.unlock ();
                }
            completedabruptly = false;
        } finally {
            processworkerexit (w, completedabruptly);
        }
   }

The above source actually did one thing: the thread that was created will repeatedly get the task from the Blockingqueue to execute after performing the task of submitting.

using a thread pool
The previous analysis of the thread pool implementation process and the source code is analyzed, below we create a thread pool and familiar with the use of the thread pool. Creating a thread pool requires several input parameters: Corepoolsize: The basic size of the thread pool. When a task is submitted to the thread pool, the line Cheng creates a thread to perform the task, but even threads with idle thread pools create new threads, which are no longer created until the number of tasks that are executed is greater than or equal to corepoolsize. Runnabletaskqueue: A blocking queue that is used to hold tasks waiting to be performed. Maximumpoolsize: The maximum number of thread pools. If the thread pool's work queue is full and the number of threads that have been created is less than the maximum number of threads, then the thread pool creates new threads to perform the task. Threadfactory: The factory used to set up thread creation. Rejectedexecutionhandler: Saturation strategy. When the queue and thread pool are full, indicating that the thread is saturated, a policy must be taken to handle the submitted task. The default policy is to reject execution, which is to throw an exception.

There are two ways to submit a task: Execute () and submit (). The difference is that the latter commits a task that has a return value. The return value can be obtained by future the object's get () method. A shutdown thread pool with the call thread pool is required after execution. There are mainly shutdown () and Shutdownnow () two methods, the principle of which is to traverse the worker threads in the thread pool, and then to call the thread in the interrupt () method. The difference is that the Shutdownnow () method first sets the state of the thread pool to stop, and then attempts to terminate the thread that is executing and the thread waiting to execute, and returns a list of tasks waiting to be executed, while the shutdown () method simply sets the thread pool state to shutdown. It then interrupts all threads that are not executing. In general, if you want to wait until the task finishes and then close the thread pool, call the shutdown () method, and then execute the Shutdownnow () method if you don't have to wait until the task finishes.

An example of a simple thread pool is as follows (recommended to build wheels):

/** * Created by Rhwayfun on 16-4-7.
    ///thread pool interface public interface Threadpool<job extends runnable> {//execute task void execute (Job job);
Closes the thread pool void shutdown ();

//Demo with thread pool package com.rhwayfun.patchwork.concurrency.r0407;
Import java.util.ArrayList;
Import java.util.List;
Import Java.util.concurrent.ArrayBlockingQueue;
Import Java.util.concurrent.BlockingQueue;

Import Java.util.concurrent.atomic.AtomicLong; public class Demothreadpool<job extends runnable> implements threadpool<job> {//blocking queues private Blockin
    Gqueue<runnable> workqueue = null; The worker thread for the save thread pool is private list<demothread> demothreadlist = collections.synchronizedlist (New arraylist<
    Demothread> ());
    Thread pool state Private Boolean isshutdown = false;
    Thread pool default size private static final int default_worker_num = 5;
    Maximum size of thread pool private static final int max_worker_num = 10;
    Thread pool smallest size private static final int min_worker_num = 1; Number of worker threads Private int worknum;

    Thread number private Atomiclong threadnum = new Atomiclong (); public demothreadpool (int num) {worknum = num > max_worker_num? Max_worker_num:num < Min_worker_num?
        Min_worker_num:num;
    Init (worknum);
    Public Demothreadpool () {init (default_worker_num); /** * thread Pool initialization * @param worknum/private void init (int worknum) {//Initializing work queues work
        Queue = new arrayblockingqueue<> (default_worker_num); Adds a specified number of worker threads to the list for (int i = 0; i < Worknum i++) {demothreadlist.add (new DemoThread (Workqueue)
        ); //Start a specified number of worker threads for (DemoThread thread:demothreadlist) {thread worker = new Thread (thread,
            "threadpool-worker-" + threadnum.incrementandget ());
            System.out.println ("threadpool-worker-" + threadnum.get () + "add to workqueue!");
        Worker.start (); }/** * Perform a task * @paramJob/@Override public void Execute (Runnable job) {if (IsShutDown) throw new IllegalStateException (
        "ThreadPool is shutdown!"); if (demothreadlist!= null) {try {///Add a task to the work queue Workqueue.put (j
                    OB);
                SYSTEM.OUT.PRINTLN ("ThreadPool receives a task!");
                catch (Interruptedexception e) {e.printstacktrace ();
            }}/** * Close thread pool/@Override public void shutdown () {IsShutDown = true;
            for (DemoThread t:demothreadlist) {t.stoptoself ();

}}//worker thread package com.rhwayfun.patchwork.concurrency.r0407;

Import Java.util.concurrent.BlockingQueue;
    public class DemoThread implements Runnable {private blockingqueue<runnable> workqueue;

    Private volatile Boolean shutdown = false; Public DemoThread (blockingqueue<runnable> WorkquEue) {this.workqueue = Workqueue; @Override public void Run () {while (!shutdown) {try {Runnable job = WORKQ
                Ueue.take ();
            Job.run ();
            catch (Interruptedexception e) {e.printstacktrace ();
        }} public void Stoptoself () {shutdown = true;
    Call the interrupt method to have the thread waiting to be queued in the work queue to return new thread from the Wait method (this). Interrupt ();

}///test program package com.rhwayfun.patchwork.concurrency.r0407;
Import Java.text.SimpleDateFormat;
Import Java.util.Date;

Import Java.util.concurrent.TimeUnit; 
        public class Demothreadpooltest {public static void main (string[] args) throws Interruptedexception {//Date Formatter
        Final SimpleDateFormat format = new SimpleDateFormat ("HH:mm:ss");
        Create a thread pool demothreadpool<demothread> threadPool = new demothreadpool<> (); Add 15 tasks for (int i = 0; i < i++) {RunnAble task = new Runnable () {@Override public void run () {System.out.
                println (Thread.CurrentThread (). GetName () + "Get Result" +format.format (new Date ());
            }
            };
        Threadpool.execute (Task);
        } threadpool.shutdown ();
        TimeUnit.SECONDS.sleep (10); System.out.println ("Work done!
    Time is "+ Format.format (new Date ());
 }
}

The results of the above procedures are:

Threadpool-worker-1 Add to workqueue!
Threadpool-worker-2 Add to workqueue!
threadpool-worker-3 Add to workqueue!
Threadpool-worker-4 Add to workqueue!
Threadpool-worker-5 Add to workqueue!
ThreadPool receives a task!
ThreadPool receives a task!
ThreadPool receives a task!
ThreadPool receives a task!
ThreadPool receives a task!
Threadpool-worker-1 Get result 19:36:48
Threadpool-worker-1 Get result 19:36:48
Threadpool-worker-1 Get result 19:36:48
Threadpool-worker-1 Get result 19:36:48
Threadpool-worker-1 Get result 19:36:48
ThreadPool receives a task!
ThreadPool receives a task!
ThreadPool receives a task!
ThreadPool receives a task!
ThreadPool receives a task!
Threadpool-worker-1 Get result 19:36:48
Threadpool-worker-1 Get result 19:36:48
Threadpool-worker-1 Get result 19:36:48
Threadpool-worker-1 Get result 19:36:48
Threadpool-worker-1 Get result 19:36:48
ThreadPool receives a task!
ThreadPool receives a task!
ThreadPool receives a task!
ThreadPool receives a task!
ThreadPool receives a task!
Threadpool-worker-1 Get result 19:36:48
Threadpool-worker-2 Get result 19:36:48
Threadpool-worker-3 Get result 19:36:48
Threadpool-worker-4 Get result 19:36:48
Threadpool-worker-5 Get result 19:36:48
Work done! Time is 19:37:03

As you can see from the implementation of the thread pool, when the client invokes execute (Runnable Task), the task is constantly added to the work queue blockingqueue, and each worker thread DemoThread constantly takes the task from the work queue, when the team column is empty , the worker thread enters the wait state. The thread calls the shutdown () method to close the thread pool after the task finishes, noting that the Stoptoself method that invoked the worker thread stops execution of the task from the work queue, except that the shutdown variable is set to False. A thread is also called in the worker thread's interrupt method, so that the purpose is to break the worker thread that is caught waiting because of a task that needs to be taken, to return it from the wait method, and then stop executing.

As you can see, the essence of the thread pool is the use of a thread-safe Work queue connection thread and client thread, where the client thread returns the task directly after it has been placed in the work queue, while the worker thread constantly pulls the task out of the work queue and executes it. When the team column is empty, all worker threads are waiting on the work queue, and any worker thread is notified when a client submits a new task, and more worker threads will wake up as the newly committed task increases.

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.