Semaphore: to achieve a flow restrictor

Focus on micro-channel public number JavaStorm

Semaphore Now commonly translated as "semaphore" maintains a set of "credentials" from the concept of speaking semaphore, a thread can access resources to obtain credentials, complete release after use, we can use a semaphore to restrict access to specific resources of concurrent threads number.

Parking spaces just like in real life, when there are vacancies in order to put into the car, otherwise they have to wait out the car is released documents.

Semaphore model

It can be simply summarized as: a counter, a queue, waiting three methods. In semaphore model, counter and wait queues outside are transparent, they can only be accessed by three methods semaphore model provided init()、acquire()、release().

  • init (): Set the initial value of the counter, the number of the initialization document. It can be understood as the number of parking spaces.
  • acquire (): value of the counter is decremented by 1; if the value of the counter at this time is less than 0, then the current thread will be blocked, waiting for being placed in a queue, or the current thread may continue to execute.
  • release (): the counter value by 1; if the value of the counter at this time is less than or equal to 0, then waiting for a wake-up thread queue and removed from the wait queue.

We mentioned here init()、acquire()、release()three methods are atomic, and this is realized by a square of the atomic semaphore model guaranteed. Inside the Java SDK, the amount of the signal model is java.util.concurrent.Semaphoreimplemented, to ensure that the class Semaphore three methods are atomic.

Ease of understanding by a simplified version of the model code signal:

public class Semaphore {
    //计数器
    private int count;
    //保存线程的等待队列
    private Queue queue;

    /**
     * 初始化计数器
     * @param count
     */
    public Semaphore(int count) {
        this.count = count;
    }

    /**
     * 获取凭证
     */
    public void acquire(){
        this.count--;
        if(this.count<0){
            // 将当前线程插入等待队列
            // 阻塞当前线程
        }
    }

    /**
     * 释放凭证
     */
    public void release(){
        this.count++;
        if(this.count >= 0) {
            // 移除等待队列中的某个线程 T
            // 唤醒线程 T
        }
    }
}

复制代码

Use semaphores

By the above principle we learned semaphore model, then you look at how you can use in the actual scene. Here we use an example to illustrate the use of accumulator semaphore it. In the case of the accumulator inside, count++the operation is a critical area, allowing only one thread of execution, that is to ensure mutually exclusive.

public class TestSemaPhore {
    private static int count;
    //初始化信号量为 1
    private static final Semaphore semaphore = new Semaphore(1);

    public static void addOne() throws InterruptedException {
        //使用信号量保证互斥,只有一个线程进入
        semaphore.acquire();
        try {
            count++;
        } finally {
            semaphore.release();
        }
    }

    public static int getCount() {
        return count;
    }

    public static void main(String[] args) throws InterruptedException {
        //模拟十个线程同时访问
        CountDownLatch countDownLatch = new CountDownLatch(1);
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                try {
                    addOne();
                    countDownLatch.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
        countDownLatch.countDown();
        TimeUnit.SECONDS.sleep(3);
        int count = getCount();
        System.out.println(count);
    }

}
复制代码

We analyze how the next semaphore to ensure mutually exclusive.

Suppose two threads T1 and T2 simultaneously access addOne(), when they are invoked semaphore.acquire();time, since this is an atomic operation, so that only one thread can semaphore counter is decremented to zero, a further thread T2 sucked decrements the counter to -1. Thread T1 corresponding to the counter is 0, satisfying 0 or greater, the thread will continue T1; T2 for the thread, the semaphore counter is -1, less than 0, the semaphore model according to our previous acquire()description, thread T2 is blocked into the waiting queue. So now only enter the critical section execution thread T1 count++.

Current semaphore counter is -1, the thread T1 when performing semaphore.release()operations performed after the counter becomes 0 +1, 0 or less satisfying by definition the model, now waiting in the queue will be awakened T2, then T2 get the opportunity to enter the code collar taken only after T1 after executing a critical section of code, thus ensuring mutually exclusive.

Achieve a flow restrictor

We use the example above implements a simple semaphore mutex, you will not be surprised, since Java SDK which provides Lock, why should a Semaphore? In fact, there is a Semaphore Lock function is not easy to achieve, and that is: Semaphore can allow multiple threads to access a critical section .

Common is the pooling of resources, such as connection pooling, object pooling, thread pools. For example, the familiar database connection pool, at the same time allows multiple threads simultaneously connected, of course, before each connection is released, allowing other threads to use.

Now we assume that there is a scene, object pooling demand, an object is created once N brother, after all these threads are multiplexed N objects, before the object is released, is not allowed to use other threads.

/**
 * 对象池
 *
 */
public class ObjectPool {
    //使用 阻塞队列保存对象池
    private final ArrayBlockingQueue<InputSaleMapDO> pool;
    //信号量
    private final Semaphore sem;

    /**
     * 初始化对象池
     *
     * @param size 池大小
     */
    public ObjectPool(int size) {
        pool = new ArrayBlockingQueue<>(size);
        sem = new Semaphore(size);
        for (int i = 0; i < size; i++) {
            InputSaleMapDO inputSaleMapDO = new InputSaleMapDO();
            inputSaleMapDO.setId((long) i);
            pool.add(inputSaleMapDO);
        }
    }

    //利用对象池的对象调用 function
    public Long run(Function<InputSaleMapDO, Long> function) throws InterruptedException {
        InputSaleMapDO obj = null;
        sem.acquire();
        try {
            obj = pool.poll();
            return function.apply(obj);
        } finally {
            pool.add(obj);
            sem.release();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ObjectPool objectPool = new ObjectPool(2);

        //模拟十个线程同时访问
        CountDownLatch countDownLatch = new CountDownLatch(1);
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                try {
                    objectPool.run(f -> {
                        System.out.println(f);
                        return f.getId();
                    });
                    countDownLatch.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
        countDownLatch.countDown();
        TimeUnit.SECONDS.sleep(30);
    }
}

复制代码

Initialize the thread pool size 2, we simulated 10 threads, each thread is allocated only two objects InputSaleMapDO.

After completion of the implementation of the callback function, they will release the object (release this work is done by pool.add (obj)), while calling release () method to update the semaphore counter. At this time, if the value in the semaphore counter is 0 or less, then there is described a thread is waiting, then automatically wake up the waiting threads.

Think

In the above example uses a stored object pool ArrayBlockingQueue, is a thread-safe container, whether it can be replaced ArrayList? Welcome back answers. There is the assumption that parking spaces as an object pool, the owner of the parking is not it also possible to use Semaphore to achieve?

Java Storm

Guess you like

Origin juejin.im/post/5d7ef114e51d4561ac7bcd64