Semaphore is typically used to limit the number of threads that can access certain resources (physical or logical), and we can set our own maximum traffic. It has two common methods, acquire () and release (), which are licensing and release licenses, respectively.
The official JDK's explanation for semaphore is this:
A count semaphore. Conceptually, semaphores maintain a set of licenses. If necessary, block each acquire () before the license is available, and then obtain the license. Each release () adds a license that may release a blocked fetch. However, instead of using the actual license object, Semaphore only counts the number of available licenses and takes action accordingly. The thread that gets the semaphore can enter the code or wait. Obtain and release access licenses through Acquire () and release ().
My explanation is this:
Semaphore equivalent to a toilet, I can create a few pits when I build a few pits, if now I have built 3 pits, now there are 10 people want to go to the toilet, then each time only 3 people, who first grabbed who went in, came out of a person, the 4th person can go in, This limits the number of toilets, that's the truth. Everyone acquire before the toilet (), if there is a pit, you can enter, not blocked, outside, etc., after the toilet, will release (), release a pit out, to ensure that the next person acquire () when there is a pit.
I think my explanation is better than the official ...
Semaphore is useful for restricting access to resources, such as restricting the number of concurrent accesses to a file, and its rationale is well understood. Here is a sample code for semaphore:
public class Semaphoretest {public static void main (string[] args) {Executorservice service = EXECUTORS.NEWCAC Hedthreadpool ();//Use concurrent libraries to create a cached thread pool final Semaphore sp = new Semaphore (3);//Create a Semaphore semaphore and set maximum concurrency of 3//avail Ablepermits ()//is used to obtain the currently available number of accesses System.out.println ("Initialize: Current" + (3-sp.availablepermits () + "concurrency")); Creating 10 tasks, the cache thread pool above creates 10 corresponding threads to execute for (int index = 0; index < index++) {final int NO = index; Record the first few tasks Runnable run = new Runnable () {//Specific task public void run () {try { Sp.acquire (); Get License System.out.println (Thread.CurrentThread (). GetName () + "Get License" + "(" +no+ ")," + "remaining:" + sp.availablepermits ()); Thread.Sleep (1000); After the visit, remember to release, or only 3 records can be printed in the console, and then the thread has been blocking sp.release (); Release license System.out.println (Thread.CurrentThread (). GetName () + "release license" + "(" +no+ ")," + "Remaining:" + Sp.availablepermits ()); } catch (Interruptedexception e) {}}}; Service.execute (run); Execute task} service.shutdown (); Close thread Pool}}
The code structure is easy to understand, 10 tasks, each time up to 3 threads to perform the task, other threads are blocked. You can view the execution of a thread by printing information:
Initialization: There are currently 0 concurrent
Pool-1-thread-1 get Permission (0), Remaining: 1
Pool-1-thread-3 get Permission (2), remaining: 0
Pool-1-thread-2 get Permission (1), Remaining: 1
Pool-1-thread-1 release License (0), Remaining: 3
Pool-1-thread-4 get Permission (3), Remaining: 1
Pool-1-thread-5 get Permission (4), Remaining: 1
Pool-1-thread-2 Release License (1), Remaining: 3
Pool-1-thread-3 Release License (2), Remaining: 3
Pool-1-thread-6 get Permission (5), remaining: 0
Pool-1-thread-4 Release License (3), Remaining: 2
Pool-1-thread-9 get Permission (8), remaining: 0
POOL-1-THREAD-5 Release License (4), Remaining: 2
POOL-1-THREAD-6 Release License (5), Remaining: 2
Pool-1-thread-8 get Permission (7), remaining: 0
Pool-1-thread-7 get Permission (6), Remaining: 2
Pool-1-thread-8 Release License (7), Remaining: 2
POOL-1-THREAD-10 get Permission (9), Remaining: 2
POOL-1-THREAD-7 Release License (6), Remaining: 2
Pool-1-thread-9 Release License (8), Remaining: 2
POOL-1-THREAD-10 Release License (9), Remaining: 3
From the results, the top three why the rest is not 3,2,1? including below, each time the remaining amount of the release seems to be wrong, is actually right, but the thread is running too fast, the first three is this: because the maximum traffic is 3, so the first three in the print statement before the execution of the Aquire () method, or partially executed, from the above results, Thread 1 is the first to go in, Thread 2 goes in, then threads 1 and 2 start printing, so there are only 1 left, and then thread 3 comes in, printing only 0 left. When released later, there may be more than one release before printing.
2. Semaphore Synchronization Issues |
I looked up from the internet, some people said Semaphore realized the synchronization function, I feel wrong, because I wrote a test code to try, and do not solve the concurrency problem, if more than one thread to operate the same data, or need to synchronize their own. Then I checked the official JDK document (to always trust the official document), which says:
Before obtaining an entry, each thread must obtain a license from the semaphore, thereby guaranteeing that the item can be used. After the thread finishes, the item is returned to the pool and the license is returned to that semaphore, allowing other threads to get the item. Note that it is not possible to maintain a synchronous lock when calling acquire () because it prevents the item from being returned to the pool. Semaphores encapsulate the required synchronization to limit access to the pool, which is separate from the synchronization required to maintain the consistency of the pool itself.
This official explanation is very clear, and then I understand that some people on the internet said the realization of synchronization means that the signal itself is packaged in the required synchronization, that is, I got one, others will not be able to get it, I release someone else to get (as I lift the hole in the toilet), But I got it. When I go to operate the public data, the synchronization semaphore for this data operation is no matter, which requires us to synchronize. Write a synchronous test code below:
public class SemaphoreTest2 {private static int data = http://blog.csdn.net/eson_15/article/details/0; public static void Main (string[] args) {Executorservice service = Executors.newcachedthreadpool (); Final Semaphore sp = new Semaphore (3); SYSTEM.OUT.PRINTLN ("Initialize: Current" + (3-sp.availablepermits () + "concurrency")); 10 tasks for (int index = 0; index < index++) {final int NO = index; Runnable run = new Runnable () {public void run () {try {//Get license Sp.acquire (); System.out.println (Thread.CurrentThread (). GetName () + "Get License" + "(" + NO + ")," + "remaining:" + SP . Availablepermits ()); Implement synchronous synchronized (Semaphoretest2.class) {System.out.println (thread.cu Rrentthread (). GetName () + "execute data before adding: data= "http://blog.csdn.net/eson_15/article/details/+ data"); data++; System.out.println (Thread.CurrentThread (). GetName () + "perform data auto-increment: Data=" http://blog.csdn . net/eson_15/article/details/+ data); } sp.release (); System.out.println (Thread.CurrentThread (). GetName () + "release license" + "(" + NO + ")," + "remaining:" + SP . Availablepermits ()); } catch (Interruptedexception e) {}}}; Service.execute (run); } service.shutdown (); }}
Look at the results of the operation (part):
Initialization: There are currently 0 concurrent
Pool-1-thread-2 get Permission (1), Remaining: 0
Pool-1-thread-2 perform data self-increment before: data=http://blog.csdn.net/eson_15/article/details/0
Pool-1-thread-3 get Permission (2), remaining: 0
Pool-1-thread-1 get Permission (0), remaining: 0
Pool-1-thread-2 perform data self-increment: DATA=HTTP://BLOG.CSDN.NET/ESON_15/ARTICLE/DETAILS/1
Pool-1-thread-3 perform data self-increment before: DATA=HTTP://BLOG.CSDN.NET/ESON_15/ARTICLE/DETAILS/1
Pool-1-thread-3 perform data self-increment: DATA=HTTP://BLOG.CSDN.NET/ESON_15/ARTICLE/DETAILS/2
Pool-1-thread-1 perform data self-increment before: DATA=HTTP://BLOG.CSDN.NET/ESON_15/ARTICLE/DETAILS/2
Pool-1-thread-7 get Permission (6), Remaining: 1
Pool-1-thread-3 Release License (2), Remaining: 2
Pool-1-thread-1 perform data self-increment: DATA=HTTP://BLOG.CSDN.NET/ESON_15/ARTICLE/DETAILS/3
As can be seen from the results, each thread will not be affected by other threads before or after the operation of the data, but other threads can obtain the license, get the license and then be blocked out, waiting for the current thread to operate. It is also possible for other threads to release the license when the current thread is manipulating data because it does not conflict at all.
If you remove the above Sync code block, try again what would be a messy result (part):
Initialization: There are currently 0 concurrent
pool-1-thread-3 get Permission (2), remaining: 0
pool-1-thread-2 get Permission (1), Remaining: 0
Pool-1-thread-3 perform data self-increment before: data=http://blog.csdn.net/eson_15/article/details/0
Pool-1-thread-2 perform data self-increment before: data=http://blog.csdn.net/eson_15/article/details/0
pool-1-thread-1 get Permission (0), Remaining: 0
pool-1-thread-3 perform data increment: data=http://blog.csdn.net/eson_15/article/details/1
Pool-1-thread-2 perform data self-increment: data=http://blog.csdn.net/eson_15/article/details/2
pool-1-thread-7 get Permission (6), Remaining: 0
Pool-1-thread-1 perform data self-increment before: data=http://blog.csdn.net/eson_15/article/details/2
Pool-1-thread-8 get Permission (7), remaining: 0
pool-1-thread-7 to perform data increment before: data=http://blog.csdn.net/eson_15/article/ details/2
Pool-1-thread-2 release License (1), Remaining: 1
pool-1-thread-7 perform data self-increment: data=http://blog.csdn.net/ ESON_15/ARTICLE/DETAILS/4
From the results, it is clear that threads 2 and 3 are all in, then the initial data is 0, thread 3 has been increased, printing 1 is fine, but thread 2? Also increased from a bit, but printed out 2. That is, thread 2 before and after the operation of the data, the data has been modified by thread 3, once again to prove that Semaphere does not realize the synchronization of the common data, in the operation of public data, we need to implement ourselves.
Semaphere If the semaphore is set to 1, then it means that only one thread at a time to operate the task, then there is no thread safety problem, so if the set signal volume of 1, you can remove the synchronized, but the efficiency is not.
The use of Semaphere summed up so much!
Related reading: http://blog.csdn.net/column/details/bingfa.html
-Willing to share and progress together!
-More articles please see: http://blog.csdn.net/eson_15
Use of the "Java Concurrency" thread Sync tool semaphore