Semaphore
is a Java concurrency package () is an important tool for controlling concurrent access to shared resources by multiple threads. It can set the number of "permits" (permit), and allows a specified number of threads to access a resource at the same time, suitable for streaming, resource pooling and other scenarios. The following from the source code design, underlying principles, application scenarios, and comparison with other JUC tools to analyze Semaphore in detail.
I. Basic Principles of Semaphore
Semaphore
It is essentially a counting semaphore that maintains an internal license count. Each thread needs to apply for a license (acquire) when it enters, and release the license (release) when it finishes. When the license count is zero, other threads will block until a thread releases the license.
1. Counting and licensing
- Number of licenses: Semaphore sets the number of permits at initialization time, usually indicating the number of threads that can access the resource at the same time.
-
count up or down: Call
acquire()
Reduce the number of licenses when callingrelease()
Increase the number of licenses when you do. - fairness: Semaphore can be set to "fair" mode, which guarantees that threads are licensed in FIFO order. The default is "non-fair" mode, which gives better performance, but the order in which threads are licensed is not guaranteed.
Second, the underlying implementation and source code analysis
Semaphore is based onAbstractQueuedSynchronizer
(AQS) implementation, which is implemented in the same way as theCountDownLatch
Similar, but uses the shared model of AQS with precise management of license counts.
1. AQS sharing model
Semaphore uses AQS's Shared mode, where AQS'sstate
Indicates the number of remaining licenses.acquireShared()
method is used to apply for a license.releaseShared()
method is used to release the license.
2. Implementation of the internal class Sync
The core implementation of Semaphore relies on its inner classesSync
Sync isAbstractQueuedSynchronizer
subclasses. There are two implementations depending on whether it is a fair pattern or not:NonfairSync
cap (a poem)FairSync
。
-
NonfairSync: Non-fair mode. Thread call
acquire
When attempting to obtain a license directly, the order is not guaranteed. - FairSync: Fair mode. Threads obtaining licenses follow the queue order.
abstract static class Sync extends AbstractQueuedSynchronizer {
Sync(int permits) {
setState(permits); // Setting the initial number of licenses
}
final int getPermits() {
return getState();
}
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 || compareAndSetState(available, remaining))
return remaining;
}
}
protected final boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();
int next = current + releases;
if (compareAndSetState(current, next))
return true;
}
}
}
-
Non-fair mode (
nonfairTryAcquireShared
): Directly try to obtain a license, and if successful update thestate
。 -
Fairness model (
FairSync
): Ensures FIFO access by following the queue order of AQS.
The use of Semaphore
Semaphore provides three main ways to manipulate licenses:
- acquire(): Get a license, or block if there is no license.
- release(): Release a license to wake up a waiting thread.
- tryAcquire(): Trying to get a license but not blocking.
Semaphore semaphore = new Semaphore(3); // initial license number is 3
// Get the license and block if no license is available
(); // initial license number is 3 // get license, block if no license available
// release the license
(); // Release the license.
// Try to get a license, return false if not available, no blocking
if (()) {
// do something
}
acquire() source code
public void acquire() throws InterruptedException {
(1);
}
acquireSharedInterruptibly(1)
will invokenonfairTryAcquireShared(1)
maybetryAcquireShared
(fair mode), tries to obtain a license, and blocks and waits if there are not enough licenses.
release() source code
public void release() {
(1);
}
releaseShared(1)
Increases the number of licenses and wakes up the blocking thread, allowing the waiting thread to continue execution.
Application Scenarios of Semaphore
Semaphore is well suited for controlling access to limited resources, typical application scenarios are:
1. Connection pool flow limiting
In database connection pooling, you can use Semaphore to limit the number of threads accessing a connection at the same time. For example, if you have a limited number of database connections, you can control the number of threads accessing them at the same time with a Semaphore to avoid overloading.
public class DatabaseConnectionPool {
private static final int MAX_CONNECTIONS = 5;
private final Semaphore semaphore = new Semaphore(MAX_CONNECTIONS);
public Connection getConnection() throws InterruptedException {
();
return acquireConnection();
}
public void releaseConnection(Connection conn) {
releaseConnection(conn);
();
}
}
2. Controlling the number of concurrent threads
In highly concurrent tasks, Semaphore can limit the number of threads executing at the same time, e.g., to control the concurrency of a download task to avoid excessive load on the system caused by too many threads.
3. Resource allocation and flow limitation
Semaphore is suitable for resource sharing scenarios, such as shared printers, thread pools with a fixed number of threads, etc., to ensure that resources are not overused.
V. Comparison with other JUC tools
1. CountDownLatch
- Licensing mechanisms: A Semaphore's license number can change dynamically, whereas a CountDownLatch's counter can only be decremented.
- Applicable Scenarios: Semaphore controls the number of concurrent resource accesses, while CountDownLatch is used for thread waiting.
2. CyclicBarrier
- corresponds English -ity, -ism, -ization: Semaphore is good for controlling the number of concurrent resource accesses, while CyclicBarrier is used as a synchronization point between threads so that all threads continue together when a certain barrier is reached.
-
dexterity: Semaphore is more flexible and can be used at any time.
release()
, without having to wait for all the threads.
3. ReentrantLock
- paradigm: ReentrantLock is an exclusive lock that allows access by only one thread at a time. semaphore allows multiple threads to share resources (multiple licenses).
- Applicable Scenarios: ReentrantLock is good for exclusive resource scenarios, Semaphore is good for resource pooling, flow limiting, etc.
4. Phaser
- complexity theory: Phaser is used for complex multi-stage synchronization where threads can be dynamically increased/decreased, while Semaphore is mainly used for fixed-quantity resource control.
- suitability: Phaser is good for phased task synchronization, while Semaphore is good for controlling the number of concurrent resources.
Mount Lushan smoke and rain Zhejiang tide, not to a thousand hates do not disappear.
To also come to nothing else, Mount Lu smoke and rain in Zhejiang tide.
There will be no more updates for nearly six months, the days of the home office are coming to an end. Read a book, the world is in front of you; do not read a book, the world is in front of you. I would like to share this with you!