Semaphore use and principle interpretation

use

overview

Semaphore (semaphore) is a concurrency control tool in Java for controlling access to shared resources. It is based on the principle of counters, which can limit the number of threads accessing a resource at the same time.

To use Semaphore in Java, you need to follow these steps:

Guide package: 

import java.util.concurrent.Semaphore;

 Create a Semaphore object:

Semaphore semaphore = new Semaphore(n);

where nis the number of threads allowed to access the shared resource concurrently.

Use the acquire() and release() methods to acquire and release the semaphore before and after the code segment that needs to access the shared resource:

try {
    semaphore.acquire(); // 获取信号量,如果没有可用的许可证,线程将被阻塞
    // 访问共享资源的代码
} catch (InterruptedException e) {
    // 处理中断异常
} finally {
    semaphore.release(); // 释放信号量,增加一个许可证
}

acquire()method attempts to acquire a license, if no license is currently available, the thread will block until one becomes available. release()method releases a license, making it available for use by other threads.

By properly using the acquire()and release()method, when the number of threads allowed by the semaphore is exceeded, the number of threads that concurrently access shared resources can be limited to achieve synchronization and mutual exclusion between threads.

It should be noted that Semaphore also provides some other methods, such as availablePermits() to obtain the number of currently available permits, and the tryAcquire() method to try to obtain a permit without blocking the thread.

concrete example 

    public static void main(String[] args) {
        // 1. 创建 semaphore 对象
        Semaphore semaphore = new Semaphore(3);
        // 2. 10个线程同时运行
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                // 3. 获取许可

                try {
                    semaphore.acquire();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                try {
                    log.debug("running...");
                    sleep(1);
                    log.debug("end...");
                } finally {
                    // 4. 释放许可

                    semaphore.release();
                }
            }).start();
        }

    }

07:35:15.485 c.TestSemaphore [Thread-2] - running...

07:35:15.485 c.TestSemaphore [Thread-1] - running...

07:35:15.485 c.TestSemaphore [Thread-0] - running...

07:35:16.490 c.TestSemaphore [Thread-2] - end...

07:35:16.490 c.TestSemaphore [Thread-0] - end...

07:35:16.490 c.TestSemaphore [Thread-1] - end...

07:35:16.490 c.TestSemaphore [Thread-3] - running...

07:35:16.490 c.TestSemaphore [Thread-5] - running...

07:35:16.490 c.TestSemaphore [Thread-4] - running...

07:35:17.490 c.TestSemaphore [Thread-5] - end...

07:35:17.490 c.TestSemaphore [Thread-4] - end...

07:35:17.490 c.TestSemaphore [Thread-3] - end...

07:35:17.490 c.TestSemaphore [Thread-6] - running...

07:35:17.490 c.TestSemaphore [Thread-7] - running...

07:35:17.490 c.TestSemaphore [Thread-9] - running...

07:35:18.491 c.TestSemaphore [Thread-6] - end...

07:35:18.491 c.TestSemaphore [Thread-7] - end...

07:35:18.491 c.TestSemaphore [Thread-9] - end...

07:35:18.491 c.TestSemaphore [Thread-8] - running...

07:35:19.492 c.TestSemaphore [Thread-8] - end...  

Source code principle analysis

Principle of lock and unlock process

Semaphore is a bit like a parking lot. Permits are like the number of parking spaces. When a thread gets the permits, it is like getting a parking space, and then the parking lot shows that the vacant parking spaces are minus one. At the beginning, the permits (state) is 3, and then there are 5 threads to get resources

Assuming that Thread-1, Thread-2, and Thread-4 cas compete successfully, but Thread-0 and Thread-3 fail to compete, and enter the AQS queue to park and block

Then Thread-0 succeeds in the competition, the permits are set to 0 again, set itself as the head node, disconnect the original head node, and unpark the next Thread-3 node, but because the permits are 0, Thread-3 is unsuccessful in trying Then enter the park state again

+

source code 

 There are two construction methods: 

  public Semaphore(int permits) {
        sync = new NonfairSync(permits);
    }

Creates a Semaphore with a given number of permissions and an unfair fairness setting.
Parameters:
licenses – the initial number of licenses available. This value may be negative, in which case a release must occur before any acquisitions can be granted. 

    public Semaphore(int permits, boolean fair) {
        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    }

Create with the given number of permissions and the given fairness settings. Semaphore
Parameters:
Licenses – The initial number of available licenses. This value may be negative, in which case a release must occur before any acquisitions can be granted.
Fairness – true if this semaphore will guarantee FIFO grants in contention, otherwise false 

    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = -2694183684443567898L;

        NonfairSync(int permits) {
            // permits 即 state

            super(permits);
        }

        // Semaphore 方法, 方便阅读, 放在此处

        public void acquire() throws InterruptedException {
            sync.acquireSharedInterruptibly(1);
        }

        // AQS 继承过来的方法, 方便阅读, 放在此处

        public final void acquireSharedInterruptibly(int arg)
                throws InterruptedException {
            if (Thread.interrupted())
                throw new InterruptedException();
            if (tryAcquireShared(arg) < 0)
                doAcquireSharedInterruptibly(arg);
        }

        // 尝试获得共享锁

        protected int tryAcquireShared(int acquires) {
            return nonfairTryAcquireShared(acquires);
        }

        // Sync 继承过来的方法, 方便阅读, 放在此处

        final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                int available = getState();
                int remaining = available - acquires;
                if (
                    // 如果许可已经用完, 返回负数, 表示获取失败, 进入 doAcquireSharedInterruptibly

                        remaining < 0 ||

                                // 如果 cas 重试成功, 返回正数, 表示获取成功

                                compareAndSetState(available, remaining)
                ) {
                    return remaining;
                }
            }
        }

        // AQS 继承过来的方法, 方便阅读, 放在此处

        private void doAcquireSharedInterruptibly(int arg) throws InterruptedException {
            final Node node = addWaiter(Node.SHARED);
            boolean failed = true;
            try {
                for (;;) {
                    final Node p = node.predecessor();
                    if (p == head) {
                        // 再次尝试获取许可

                        int r = tryAcquireShared(arg);
                        if (r >= 0) {
                            // 成功后本线程出队(AQS), 所在 Node设置为 head

                            // 如果 head.waitStatus == Node.SIGNAL ==> 0 成功, 下一个节点 unpark

                            // 如果 head.waitStatus == 0 ==> Node.PROPAGATE

                            // r 表示可用资源数, 为 0 则不会继续传播

                            setHeadAndPropagate(node, r);
                            p.next = null; // help GC

                            failed = false;
                            return;
                        }
                    }
                    // 不成功, 设置上一个节点 waitStatus = Node.SIGNAL, 下轮进入 park 阻塞

                    if (shouldParkAfterFailedAcquire(p, node) &&

                            parkAndCheckInterrupt())
                        throw new InterruptedException();
                }
            } finally {
                if (failed)
                    cancelAcquire(node);
            }
        }

        // Semaphore 方法, 方便阅读, 放在此处

        public void release() {
            sync.releaseShared(1);
        }

        // AQS 继承过来的方法, 方便阅读, 放在此处

        public final boolean releaseShared(int arg) {
            if (tryReleaseShared(arg)) {
                doReleaseShared();
                return true;
            }
            return false;
        }

        // Sync 继承过来的方法, 方便阅读, 放在此处
        protected final boolean tryReleaseShared(int releases) {
            for (;;) {
                int current = getState();
                int next = current + releases;
                if (next < current) // overflow
                    throw new Error("Maximum permit count exceeded");
                if (compareAndSetState(current, next))
                    return true;
            }
        }
    }

Guess you like

Origin blog.csdn.net/m0_62436868/article/details/131453991