Several implementations of Java thread pool and the explanation of common problems

Source: Internet
Author: User

Threads are often involved in the work. For example, some tasks are often handed out to the thread to execute asynchronously. or a server-side program establishes a single thread-processing task for each request. Outside the thread, such as the database connection we used. These create destroy or turn off operations that greatly affect system performance. So, the usefulness of "pool" is highlighted.

1. Why use a thread pool

In the implementation described in Section 3.6.1, a new worker thread is assigned to each customer. The thread is destroyed when the worker's communication with the client ends. This approach has the following deficiencies:

    • The cost of server creation and destruction (including time spent and system resources) is large. This is not an explanation, you can check the "thread creation process." In addition to the work done by the machine itself, we have to instantiate and start, which all need to occupy the stack resources.
    • In addition to the overhead of creating and destroying threads, the active thread consumes system resources. this should be the consumption of the stack resource, guess the database connection number set a reasonable value, also have this consideration.
    • If the number of threads is fixed, and each thread has a long declaration period, then thread switching is also relatively fixed. Different operating systems have different switching cycles, typically around 20ms. The switch here is that the CPU is transferred between the threads under the JVM and the underlying operating system scheduling. If you frequently create and destroy threads, you will switch threads frequently, because once a thread is destroyed, it is necessary to give up the right to a thread that is already ready, so that the thread gets the chance to run. In this case, the switch between threads does not follow the fixed switching cycle of the system, and the overhead of switching threads is even greater than the cost of creating and destroying them.

In contrast, using the thread pool, you pre-create threads that constantly pull tasks from the work queue and then perform that task. When a worker thread finishes a task, it continues to perform another task in the work queue. The advantages are as follows:

    • The number of creation and destruction times is reduced, and each worker thread can be reused and able to perform multiple tasks.
    • According to the system's carrying capacity, it is convenient to adjust the number of threads in the thread pool to prevent the system from crashing because of excessive system resource consumption.

2. Simple implementation of thread pool

Here is a simple thread pool of your own writing, which is also directly tapped from the Java Network Programming book.

 Packagethread;Importjava.util.LinkedList;/*** thread pool implementation, according to the regular thread pool length, maximum length, queue length, we can increase the number of limit implementations *@authorHan*/ Public classMythreadpoolextendsthreadgroup{//number of CPUs---runtime.getruntime (). Availableprocessors (); //whether to close    Private BooleanisClosed =false; //Queue    PrivateLinkedlist<runnable>WorkQueue; //thread pool ID    Private Static intThreadpoolid; Private intThreadID;  PublicMythreadpool (intpoolsize) {        Super("Mythreadpool." +threadpoolid); Threadpoolid++; Setdaemon (true); WorkQueue=NewLinkedlist<runnable>();  for(inti = 0;i<poolsize;i++){            NewWorkthread (). Start (); }    }    //this can be replaced with concurrentlinkedqueue, you can avoid the use of synchronized efficiency problems     Public synchronized voidExecute (Runnable Task) {if(isClosed) {Throw NewIllegalStateException ("Connection pool is closed ..."); }Else{workqueue.add (Task);        Notify (); }    }        protected synchronizedRunnable Gettask ()throwsinterruptedexception { while(workqueue.size () = = 0){            if(isClosed) {return NULL;        } wait (); }        returnWorkqueue.removefirst (); }         Public synchronized voidClose () {if(!isClosed) {isClosed=true;            Workqueue.clear ();        Interrupt (); }    }         Public voidjoin () {synchronized( This) {isClosed=true;        Notifyall (); } thread[] Threads=NewThread[activecount ()]; intCount =Enumerate (threads);  for(inti = 0;i<count;i++){            Try{threads[i].join (); } Catch(Exception e) {}}} classWorkthreadextendsthread{ PublicWorkthread () {Super(Mythreadpool. This, "Workthread" + (threadid++)); System.out.println ("Create ..."); } @Override Public voidrun () { while(!isinterrupted ()) {System.out.println ("Run.."); Runnable Task=NULL; Try {                    //This is a blocking methodTask =Gettask (); } Catch(Exception e) {}if(Task! =NULL) {task.run (); }Else{                     Break; }            }        }    }}

The thread pool primarily defines a work queue and some pre-created threads. You can submit a task to a thread whenever the Execute method is called.

The latter thread blocks in Gettask () when there is no task, until a new task comes in and wakes up.

Both join and close can be used to close the thread pool. The difference is that the join executes the task in the queue, and close clears the queue immediately and interrupts all worker threads. The interrupt () in close () is equivalent to calling the respective interrupt () in Threadgroup that contains the child threads, so the thread is either in wait or sleep, and is thrown interruptexception

The test classes are as follows:

 Public classTestmythreadpool { Public Static voidMain (string[] args)throwsinterruptedexception {Mythreadpool pool=NewMythreadpool (3);  for(inti = 0;i<10;i++) {Pool.execute (NewRunnable () {@Override Public voidrun () {Try{Thread.Sleep (1000); } Catch(Interruptedexception e) {} System.out.println ("Working ...");        }            });        } pool.join (); //pool.close ();    }}

3. The thread pool provided by the JDK class library

Java provides a good thread pool implementation that is more robust and efficient than our own implementations, but also more powerful.

The class diagram is as follows:

With regard to this kind of thread pool, our predecessors have been very well explained. Any Baidu under the Java thread pool, have written a very detailed examples and tutorials, here will not repeat.

Java comes with thread pool and queue detail

4. Spring Injection thread pool

When using the spring framework, if we use Java-provided methods to create thread pools, it is very inconvenient to manage in multithreaded applications and does not conform to our idea of using spring. (although spring can be injected by static methods)

In fact, spring itself provides a good thread pool implementation. This class is called threadpooltaskexecutor.

The configuration in spring is as follows:

<BeanID= "Executorservice"class= "Org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">        < Propertyname= "Corepoolsize"value= "${threadpool.corepoolsize}" />        <!--The thread pool maintains a minimum number of threads -        < Propertyname= "Keepaliveseconds"value= "${threadpool.keepaliveseconds}" />        <!--the thread pool maintains idle time allowed by threads -        < Propertyname= "Maxpoolsize"value= "${threadpool.maxpoolsize}" />        <!--maximum number of threads maintained by the thread pool -        < Propertyname= "Queuecapacity"value= "${threadpool.queuecapacity}" />        <!--buffer queue used by the thread pool -    </Bean>

5. Considerations for using the thread pool
    • Dead lock

Any multithreaded program has the risk of deadlock, the simplest case is two thread ab,a hold lock 1, request lock 2,b hold lock 2, request lock 1. (In this case, the exclusive lock on MySQL will also appear, not the database will directly indicate the error). There is another deadlock in the thread pool: Assume that all worker threads in the thread pool are blocked while they are performing their respective tasks, and they are waiting for the result of the execution of a task A. Task A is in the queue and cannot be executed because there are no idle threads. This way all the resources of the thread pool will be blocked and deadlocks will be created.

    • Insufficient system resources

If the number of threads in the thread pool is very large, these threads consume a significant amount of resources, including memory and other system resources, which can severely affect system performance.

    • Concurrency errors

The work queue of the thread pool relies on the wait () and notify () methods to make the worker thread get the task in time, but these two methods are difficult to use. If the code is wrong, notifications may be lost, causing the worker thread to remain idle for a while, ignoring the tasks that need to be handled in the work queue. Because it is best to use some more mature thread pools.

    • Thread leaks

A serious risk of using the thread pool is a thread leak. For thread pools that have a fixed number of worker threads, if a worker thread throws runtimeexception or error while executing a task, and those exceptions or errors are not captured, the worker thread terminates abnormally, leaving the thread Chengtiyongju missing. (This is too interesting)

In another case, a worker is blocked while executing a task, and if it waits for the user's input data, but the user has never entered the data, the thread has been blocked. Such a worker thread is in its own right, and it does not actually perform any tasks. If all the threads in the thread pool are in this state, the thread pools cannot join the new task.

    • Task overload

When a worker queue has a large number of tasks queued for execution, these tasks themselves can consume too much system resources and cause resource shortages.

In summary, when using the thread pool, follow these guidelines:

  1. If task a needs to synchronously wait for the execution of task B during execution, task A is not appropriate for joining the worker queue in the thread pool. If you want to wait for other task execution results to join in the queue like task A, it can cause a deadlock
  2. If a task can be blocked and is blocked for a long time, you should set a time-out and avoid a thread leak because the worker thread is permanently blocked. In a server-only program, when a thread waits for a client to connect, or waits for data sent by a customer, it can cause blocking, setting the time by calling the Setsotimeout method of ServerSocket and setting the time-out for waiting for a client connection. For each socket connected to the client, call the Setsotimeout method of the socket and set the time-out period waiting for the customer to send the data.
  3. understand the nature of the task , which is whether execution often blocks IO operations or performs operations that have not been blocked. The former consumes CPU while the latter has higher utilization. It is estimated how long it will take to complete the task, whether it is a short or long time task, then classify the task according to the characteristics of the task, and then add different types of tasks to the work queue of the different thread pool, so that we can allocate and adjust each thread pool according to the characteristics of the task.
  4. adjusts the size of the thread pool . The optimal size of the thread pool depends primarily on the number of available CPUs for the system and the characteristics of the tasks in the work queue. If there is only one task queue on a system with n CPUs, and all of them are operational (not blocking) tasks, then when the thread pool has n or n+1 worker threads, it generally gets the maximum CPU usage. If the work queue contains tasks that will perform IO operations and are frequently blocked, make the thread pool larger than the number of available CPUs because not all worker threads are working. Select a typical task, and then estimate the ratio of wait time to the amount of time it takes to actually consume the CPU in the project that performs this task wt/st. For a system with n CPUs, approximately n (1+wt/st) threads need to be set to ensure that the CPU is fully utilized. Of course, CPU utilization is not the only thing to consider during the thread pooling process, and as the number of threads in the pool grows, there are limits to memory or other resources, such as sockets, open file handles, or number of database connections. Ensure that the system resources consumed by multithreading are within the scope of the system.
  5. Avoid task overloading . The server should limit the number of concurrent connections to the client based on the system's load capacity. When a customer's connection exceeds the limit, the server can reject the connection, make friendly prompts, or limit the queue length.

Several implementations of Java thread pool and the explanation of common problems

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.