Java multithreading - concurrency tools Semaphore Comments

Brief introduction

Semaphore is a synchronization aid, which translates to a semaphore used to implement flow control, it can control the number of access to resources within the same time.

Whether Synchroniezd or ReentrantLock, once only allow one thread to access a resource, but Semaphore can specify multiple threads simultaneously access a particular resource.

Semaphore has a constructor, you may pass an int type integer n, represents a piece of code at most n threads can access, if exceeded n, then please wait until a thread completes this code block, the next thread re-entry.

Defines two semaphore operations:

  • acquire (get): When a thread calls acquire the operation, it either successfully acquired the semaphore (signal minus 1), or keep on waiting until the thread releases the semaphore, or timeout, internal Semaphore will maintain a wait queue with these are stored in a suspended thread.
  • release (release) actually will semaphore value of +1, and then wake up a waiting thread waiting queue corresponding to any instance of Sepmaphore.

Scenarios

Signal primarily serves two purposes:

  • A plurality of mutually exclusive use of shared resources.
  • Used to control the number of concurrent threads.

example

The following example: 5 threads grab three parking spaces, but only a maximum of three threads to grab parking spaces, and so other threads release the semaphore, to grab parking spaces.

	public static void main(String[] args) {
		Semaphore semaphore = new Semaphore(3);

		for (int i = 0; i < 5; i++) {
			new Thread(new Runnable() {
				@Override
				public void run() {
					try {
						semaphore.acquire();//申请资源
						System.out.println(Thread.currentThread().getName()+"抢到车位");
						ThreadUtil.sleep(RandomUtil.randomInt(1000,5000));
						System.out.println(Thread.currentThread().getName()+"归还车位");
					} catch (InterruptedException e) {
						e.printStackTrace();
					}finally {
					    //释放资源
						semaphore.release();
					}

				}
			},"线程"+i).start();
		}
	}
复制代码

Precautions

  • Semaphore.acquire () and Semaphore.release () is always used in pairs, and this point needs to ensure that by the application code itself.
  • Case Semaphore.release () calls should be placed in the finally block, has been an exception to avoid the application code, the current thread amount signal obtained can not be returned.
  • If the value of the parameter permits Semaphore constructor is set to 1, corresponding to create a Semaphore mutex. Mutex different from the other, this thread releases the mutex allows the locks held by another thread the corresponding Semaphore.release because the next thread can be executed not Semaphore.acquire () of the case ().
  • By default, non-Semaphore fairness scheduling strategy adopted.

principle

 abstract static class Sync extends AbstractQueuedSynchronizer {
    //省略
 }
复制代码

Semaphore class internal use Sync, Sync is inherited AbstractQueuedSynchronizer, so Sync bottom or use AQS implemented .Sync two implementation classes NonfairSync and FairSync, used to specify whether to adopt a fair policy when acquiring the semaphore.

Initialization method

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


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

Sync(int permits) {
    setState(permits);
}
复制代码

As described above, the default non Semaphore fairness policy, if required fairness policy may be used with a two-argument constructor to construct Semaphore objects.

Permits parameters are passed to the state of the AQS value that represents the number of the semaphore is currently held.

void acquire () method

The purpose of the current thread calls the method is to acquire a semaphore resources.

If the number is greater than the amount of the current signal is 0, the current count of the semaphore is decremented by one, then the method returns. Otherwise, if the current number of semaphore 0, then the current thread will be placed AQS blocking queue. When another thread calls interrupt the current thread () method interrupts the current thread, the current thread will throw InterruptedException exception return.

//Semaphore方法
public void acquire() throws InterruptedException {
    //传递参数为1,说明要获取1个信号量资源
    sync.acquireSharedInterruptibly(1);
}

//AQS的方法
public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
    //(1)如果线程被中断,则抛出中断异常
    if (Thread.interrupted())
        throw new InterruptedException();
    //(2)否则调用Sync子类方法尝试获取,这里根据构造函数确定使用公平策略
    if (tryAcquireShared(arg) < 0)
        //如果获取失败则放入阻塞队列.然后再次尝试,如果使用则调用park方法挂起当前线程
        doAcquireSharedInterruptibly(arg);
}
复制代码

As seen from the code, Acquire () calls within the Sync acquireSharedlnterruptibly method, which will respond to the interrupt (if the current thread is interrupted, then the interrupt exception thrown). Attempts to acquire a semaphore AQS resources is achieved by a method tryAcquireShared Sync subclass, so here are discussed in two ways.

TryAcquireShared first discuss the method of non-equity strategies NonfairSync class code is as follows:

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

final int nonfairTryAcquireShared(int acquires) {
    for (;;) {
        //获取当前信号量值
        int available = getState();
        //计算当前剩余值
        int remaining = available - acquires;
        //如果当前剩余值小于0或则CAS设置成功则返回
        if (remaining < 0 ||
            compareAndSetState(available, remaining))
            return remaining;
    }
}
复制代码

Code above to obtain the current signal magnitude (Available), then subtracting the value to be obtained (Acquires), to obtain the number of remaining signal (Remaining) and, if the residual value is less than the number of 0 indicates the amount of signal current to satisfy demand, then directly back negative, then the current thread will be placed AQS blocking queue is suspended. If the residual value is greater than 0, the CAS operation to set the current value for the remaining signal magnitude, and then returns the remaining value.

In addition, due to the non-NonFairSync equitable access, that first call aquire method to obtain the semaphore thread not necessarily better than the later ones to get first semaphore.

Consider the following scenario, if thread A first call aquire () method to get the semaphore, but the current number of semaphore is 0, then the thread will be placed AQS A blocking queue. After some time the thread C calls release () method releases a semaphore, if no other thread gets the semaphore, the thread A is activated, then acquire the semaphore, but after the release of the semaphore if the thread C, thread C calls aquire method, the thread and thread a to C will compete this semaphore resources. If a non-fairness policy, seen from nonfairTryAcquireShared code C after the thread before the thread A can be activated, or the activation of the first signal is obtained in an amount of A thread, the thread is blocked thread in this mode and the current request It is a competitive relationship, rather than follow the first come first served policy.

Look FairSync class fairness Here is how to ensure fairness.

protected int tryAcquireShared(int acquires) {
    for (;;) {
        //查询是否当前线程节点的前驱节点也在等待获取该资源,有的话直接返回
        if (hasQueuedPredecessors())
            return -1;
        int available = getState();
        int remaining = available - acquires;
        if (remaining < 0 ||
            compareAndSetState(available, remaining))
            return remaining;
    }
}
复制代码

Visible fairness or rely on this function to ensure the hasQueuedPredecessors. So Semaphore fairness policy is whether the current thread node precursor nodes are waiting to acquire the resources, and if so to give up the privileges acquired, then the current thread will be placed in a queue AQS blocked, or he would get.

void acquire (int permits) Method

The method and acquire () different methods, which only needs to obtain a signal magnitude, while the former permits a acquired.

public void acquire(int permits) throws InterruptedException {
    if (permits < 0) throw new IllegalArgumentException();
    sync.acquireSharedInterruptibly(permits);
}
复制代码

void acquireUninterruptibly()方法

This method acquire () is similar, except that the method of interruption does not respond, that is, when the current thread calls when acquireUninterruptibly access to resources (included after being blocked), the other thread calls interrupt the current thread () method sets the current thread interrupt flag, then the current thread does not throw an exception return IllegalArgumentException.

public void acquireUninterruptibly(int permits) {
    if (permits < 0) throw new IllegalArgumentException();
    sync.acquireShared(permits);
}
复制代码

void release () method

The role of this method is that the magnitude of the signal current Semaphore object increased by 1 if the current thread because there are calls aquire method is blocked from being put into the AQS blocking queue, a signal will be selected according to the number of the amount of equity strategy be satisfied thread activation, activation thread attempts to get just the amount of signal gain.

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

public final boolean releaseShared(int arg) {
    //(2)尝试释放资源
    if (tryReleaseShared(arg)) {
        //(3)资源释放成功则调用park方法唤醒AQS队列里面最先挂起的线程
        doReleaseShared();
        return true;
    }
    return false;
}

protected final boolean tryReleaseShared(int releases) {
    for (;;) {
        //获取当前信号量值
        int current = getState();
        //将当前信号量值增加releases,这里为增加1
        int next = current + releases;
        //移除处理
        if (next < current) // overflow
            throw new Error("Maximum permit count exceeded");
        //使用CAS保证更新信号量值的原子性
        if (compareAndSetState(current, next))
            return true;
    }
}
复制代码

By the code release () -> sync.releaseShared (1), can be seen, each release method only increases the magnitude of the signal 1, tryReleaseShared method is an infinite loop, CAS is used to ensure that the method of release of the semaphore operation atomic increment 1 after .tryReleaseShared method to increase the signal magnitude successfully executes the code (3), that is the way to activate AQS call because the call to acquire methods blocked thread.

void release(int permits)方法

This method differs from the release method without parameters, the former permits every call increases the magnitude of the original signal on the basis of the latter increments l.

public void release(int permits) {
    if (permits < 0) throw new IllegalArgumentException();
    sync.releaseShared(permits);
}
复制代码

Also you can see, sync.releaseShared here is sharing method, indicating that the semaphore is shared by the threads, semaphores and fixed without binding threads, multiple threads may be used to update the value of the CAS signal without being at the same time the amount of obstruction.

Guess you like

Origin juejin.im/post/5e204a2a518825367841d40d