Objective
In a multithreaded environment, the JDK provides developers with a number of components for user use (mainly under java.util.concurrent), so that users do not need to be concerned about how to write code that takes into account both thread safety and efficiency in a specific scenario. Before the thread pool, Blockingqueue are under the Java.util.concurrent component, although the timer is not under the java.util.concurrent, but it is also considered. The latter two articles will briefly explain the use of other components in multiple threads in the form of examples, without the need for a deep understanding of how each component will function.
This article mainly explains is Countdownlatch, Semaphore, Exchanger.
Countdownlatch
The main mechanism provided by Countdownlatch is to trigger an event when more than one thread (the value of the count parameter is equal to the initialization Countdownlatch) has reached the expected state or when the expected work is done, and other threads can wait for the event to trigger their own follow-up work. It is important to note that Countdownlatch is capable of waking up multiple waiting threads.
The thread that reaches its expected state calls the Countdownlatch countdown method, and the waiting thread calls the Countdownlatch await method. If the count value of Countdownlatch initialization is 1, then this is degraded to a single event, that is, a thread notifies other threads, and the effect is equal to the object's wait and Notifyall,count values greater than 1 are common ways The goal is to have multiple threads reach their expected state, become an event notification, and the thread continue its behavior.
See an example:
Private Static classWorkthreadextendsthread{PrivateCountdownlatch CDL; Private intSleepsecond; PublicWorkthread (String name, Countdownlatch CDL,intSleepsecond) { Super(name); This. CDL =CDL; This. Sleepsecond =Sleepsecond; } Public voidrun () {Try{System.out.println ( This. GetName () + "started, Time is" +System.currenttimemillis ()); Thread.Sleep (Sleepsecond* 1000); Cdl.countdown (); System.out.println ( This. GetName () + "done, time is" +System.currenttimemillis ()); } Catch(interruptedexception e) {e.printstacktrace (); } }} Private Static classDonethreadextendsthread{PrivateCountdownlatch CDL; PublicDonethread (String name, Countdownlatch CDL) {Super(name); This. CDL =CDL; } Public voidrun () {Try{System.out.println ( This. GetName () + "to wait, time for" +System.currenttimemillis ()); Cdl.await (); System.out.println ( This. GetName () + "Wait is over, time is" +System.currenttimemillis ()); } Catch(interruptedexception e) {e.printstacktrace (); } }} Public Static voidMain (string[] args)throwsexception{Countdownlatch CDL=NewCountdownlatch (3); Donethread dt0=NewDonethread ("DoneThread1", CDL); Donethread DT1=NewDonethread ("DoneThread2", CDL); Dt0.start (); Dt1.start (); Workthread wt0=NewWorkthread ("WorkThread1", CDL, 2); Workthread Wt1=NewWorkthread ("WorkThread2", CDL, 3); Workthread Wt2=NewWorkthread ("WorkThread3", CDL, 4); Wt0.start (); Wt1.start (); Wt2.start ();}
Look at the results of the operation:
DoneThread2 to wait, time for 1444563077434donethread1 to wait, time for 1444563077434workthread1 started, time for 1444563077434workthread3 started, Time for 1444563077435workthread2 started, time for 1444563077435workthread1 execution, time for 1444563079435workthread2 executed, Time for 1444563080435workthread3 execution finished, time for 1444563081435donethread1 wait, time for 1444563081435donethread2 wait is finished, Time is 1444563081435
The effect is very obvious, explain:
1. Start 2 threads donethread thread wait 3 workthread all finished
2, 3 Workthread all finished, the last execution of the WorkThread3 executed a second in line with the expected
3, the last three sentences from time to see almost at the same time, indicating that Countdownlatch set to 3,workthread3 execution, two wait for the thread immediately executes the following code
This is equivalent to an evolutionary version of the wait/notification mechanism, which can be implemented after multiple worker threads have completed a task to notify multiple waiting threads to start working, before a worker thread finishes the task to notify a waiting thread or a worker thread to complete the task to notify all waiting threads.
Countdownlatch is actually useful, especially for this scenario where a problem is divided into n parts, and after all the sub-parts are complete, notify another/several threads to start working. For example, I want to statistics C, D, E, f files, you can open 4 threads, respectively, statistics C, D, E, F disk files, statistical completion of the file information summarized into another/several threads for processing
Semaphore
Semaphore is a very useful component that is equivalent to a concurrent controller and is used to manage semaphores. When constructing a value that is passed to the managed semaphore, this value is the control of the concurrency, we need to control the concurrency code, before execution through the acquire method to obtain the signal, after execution through release to return the signal. Each time the acquire returns successfully, the semaphore available semaphore will be reduced by one, and if no signal is available, the acquire call will block and wait for the release call to free the signal before acquire will get the signal and return.
The semaphore is divided into single-valued and multi-valued two types:
1, the single-valued semaphore management of only 1 semaphores, the semaphore can only be 1, can only be obtained by a thread, meaning that the concurrent code can only be run by one thread, which is equivalent to a mutual exclusion lock
2, multi-value semaphore management of the signal amount of 1, mainly used to control the number of concurrent
Take a look at the code example:
Public Static voidMain (string[] args) {FinalSemaphore Semaphore =NewSemaphore (5); Runnable Runnable=NewRunnable () { Public voidrun () {Try{semaphore.acquire ();
System.out.println (Thread.CurrentThread (). GetName ()+ "Get the semaphore, time is" +System.currenttimemillis ()); Thread.Sleep (2000); System.out.println (Thread.CurrentThread (). GetName ()+ "Release the semaphore, Time is" +System.currenttimemillis ()); } Catch(interruptedexception e) {e.printstacktrace (); } finally{semaphore.release (); } } }; thread[] Threads=NewThread[10]; for(inti = 0; i < threads.length; i++) Threads[i]=NewThread (runnable); for(inti = 0; i < threads.length; i++) Threads[i].start ();}
Look at the results of the operation:
1thread-1 received the semaphore, the time is 14445570404642thread-2 received the semaphore, the time is 14445570404653thread-0 received the semaphore, the time is 14445570404644thread-3 received the semaphore, the time is 14445570404655thread-4 received the semaphore, the time is 14445570404656thread-2 Release The semaphore, Time is 14445570424667thread-4 Release The semaphore, Time is 14445570424668thread-0 Release The semaphore, Time is 14445570424669thread-1 Release The semaphore, Time is 1444557042466Tenthread-3 Release The semaphore, Time is 1444557042466 Onethread-9 received the semaphore, the time is 1444557042467 Athread-7 received the semaphore, the time is 1444557042466 -thread-6 received the semaphore, the time is 1444557042466 -thread-5 received the semaphore, the time is 1444557042466 thethread-8 received the semaphore, the time is 1444557042467 -thread-9 Release The semaphore, Time is 1444557044467 -thread-6 Release The semaphore, Time is 1444557044467 -thread-7 Release The semaphore, Time is 1444557044467 +thread-5 Release The semaphore, Time is 1444557044468 -Thread-8 released the semaphore, Time is 1444557044468.
The first 10 behavior part, the thread that runs is 1 2 0 3 4, see the time difference also is the code contract 2 second, the post 10 behavior part, the thread that runs is 9 7 6 5 8, Time difference also is the contract 2 second, this manifests the function of semaphore.
This method of controlling concurrent concurrency by Semaphore is less granular than controlling the number of threads to control the number of concurrent numbers, because semaphore can control the concurrency of code blocks by means of acquire and release methods.
Last note two points:
1, Semaphore can specify a fair lock or a non-fair lock
2, acquire method and release method can have parameters, indicating the number of acquisition/return of the semaphore
Exchanger
Exchanger, in the name of the understanding is the exchange. The exchanger is used to exchange data between two threads, noting that data exchange can only be done between two threads. The thread blocks on the exchanger exchange method until another thread has reached the same exchanger exchange method, exchanging data, and then two threads continue to execute their own related code.
Exchanger has only one exchange method for exchanging data. Take a look at the example:
Public Static classExchangerthreadextendsthread{PrivateString str; PrivateExchanger<string>Exchanger; Private intSleepsecond; PublicExchangerthread (String str, exchanger<string> Exchanger,intSleepsecond) { This. str =str; This. Exchanger =Exchanger; This. Sleepsecond =Sleepsecond; } Public voidrun () {Try{System.out.println ( This. GetName () + "Start, the original data is" + str + ", the time is" +System.currenttimemillis ()); Thread.Sleep (Sleepsecond* 1000); STR=Exchanger.exchange (str); System.out.println ( This. GetName () + "Exchange data, after Exchange data is" + str + ", Time is" +System.currenttimemillis ()); } Catch(interruptedexception e) {e.printstacktrace (); } }} Public Static voidMain (string[] args) {Exchanger<String> exchanger =NewExchanger<string>(); Exchangerthread et0=NewExchangerthread ("111", exchanger, 3); Exchangerthread Et1=NewExchangerthread ("222", Exchanger, 2); Et0.start (); Et1.start ();}
Look at the results of the operation:
thread-0 Start, the original data is 111, time is 1444560972303Thread-1 start, the original data is 222, time is 1444560972303Thread-0 Exchange data, The data after Exchange is 222, time is 1444560975303Thread-1 Exchange data, the data after Exchange is 111, time is 1444560975303
See two threads exchanged data, because a thread sleeps 2 seconds, a thread sleeps 3 seconds, since to exchange data, must sleep 2 seconds to wait for 3 seconds to sleep, so see the time difference is 3000ms is 3s.
From this example, exchanger is a bit like the two-way form of Java multithreading 15:queue, Blockingqueue, and Synchronousqueue using Blockingqueue to implement the producer/consumer model article. It may be useful in genetic algorithms and piping design.
Java Multi-Threading 20: Countdownlatch, Semaphore, Exchanger for other components under multithreading