Multithreading - CountDownLatch tools of concurrent Detailed

Brief introduction

Often encounter the need to turn in your daily development in the main thread multiple threads to execute in parallel tasks, and the main thread needs to wait for the scene to summarize the thread is finished after all the child.

One common use of CountDownLatch appear before the thread join () method to achieve this, but join method is not flexible enough, can not meet the needs of different scenes, so the JDK provides CountDownLatch this class.

CountDownLatch aid is a synchronous, allows a thread to wait or more, a group of operation until the other thread of execution is completed .

CountDownLatch There are two main methods, when one or more thread calls await method, these thread is blocked. Other thread calls countDown method will counter by 1, when the value of the counter becomes zero, because await method blocks thread will be woken up, continue.

Note: This is a one-time tool that can not be reset count .

If you need to be able to reset the count, we will use CyclicBarrier.

Scenarios

  1. Wait for all child thread is finished, the main thread and then execute down.
  2. All thread to wait until after the latch count is 0, then execute simultaneously.

example

The following example is the use of CountDownLatch achieve all child threads started simultaneously, the main thread wait for all children after the implementation of all the threads, the main thread and then execute down.

public class CountDownLatchDemo {
	public static void main(String[] args) throws InterruptedException {
		CountDownLatch startLatch = new CountDownLatch(1);
		CountDownLatch downLatch = new CountDownLatch(5);

		for (int i = 0; i < 5; i++) {
			new Thread(new worker(startLatch,downLatch),"线程:"+i).start();
		}
		TimeUnit.SECONDS.sleep(1); //休眠1秒,保证所有线程已经调用了await方法
		System.out.println("所有任务都已启动");
		startLatch.countDown();
		System.out.println("等待所有任务完成");
		//主线程阻塞等待,直到所有子线程完成
		downLatch.await();
		System.out.println("所有任务完成");
	}
}

class worker implements Runnable{

	private CountDownLatch startLatch;
	private CountDownLatch downLatch;

	public worker(CountDownLatch startLatch, CountDownLatch downLatch) {
		this.startLatch = startLatch;
		this.downLatch = downLatch;
	}
	@Override
	public void run(){
		System.out.println(Thread.currentThread().getName()+"准备工作完成");
		try {
			startLatch.await(); //等待所有工人准备完毕后再开始工作
		} catch (Exception e) {
			e.printStackTrace();
		}
		work();
		try {
			System.out.println(Thread.currentThread().getName()+"工作完成");
			downLatch.countDown();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void work() {
		System.out.println(Thread.currentThread().getName()+"开始工作");
		try {
			TimeUnit.SECONDS.sleep(RandomUtil.randomInt(5));
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

Print Results:

Print results

CountDownLatch differences and join methods

One difference is that calling a child thread join after () method, the thread will remain blocked until the child thread has finished running, and CountDownLatch use the counter to allow the child thread has finished running or count down in the operation, which is CountDownLatch can any time the child thread running so that await method returns without necessarily having to wait until the end of the thread.

In addition, usually added directly use the thread pool to manage threads Runable to the thread pool, this time there is no way to re-join method of calling thread, and that is countDownLatch compared to join us for thread synchronization method allows more flexible control.

The principle CountDownLatch

From CountDownLatch name on it you should have to guess its internal counter and the counter is decremented.

private final Sync sync;

private static final class Sync extends AbstractQueuedSynchronizer {
    //省略
}

CountDownLatch internal use Sync is this internal static class to achieve, Sync also inherited AbstractQueuedSynchronizer, so the bottom is achieved using AQS .

Common methods:

Constructor

By following constructors, it can be found, in fact, the value of the counter is assigned to the state variable initialization State of AQS, is herein used to represent the state of the AQS value counter value .

Note: When CountDownLatch initialization must be given count, the number used to define the number of threads ready blocked.

public CountDownLatch(int count) {
    if (count < 0) throw new IllegalArgumentException("count < 0");
    this.sync = new Sync(count);
}

Sync(int count) {
    setState(count);
}

void await () method

When the thread calls await method CountDownLatch object, the current thread will be blocked until one of the following happens will return:

  • When all threads call a method countDown CountDownLatch objects, that is, when the counter value is 0;
  • Other thread calls interrupt the current thread () method interrupts the current thread, the current thread will throw InterruptedException exception and then return.

The following look at how the internal method is to call the AQS await () is:

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

You can see from the above code, await () method calls the delegate sync acquireSharedInterruptibly method of AQS.

The latter code is as follows:

//AQS获取共享资源时可被中断的方法
public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
    //如果线程被中断则抛出异常
    if (Thread.interrupted())
        throw new InterruptedException();
    //查看当前计数器值是否为0,
    //若为0,tryAcquireShared方法返回1,不符合if条件,则直接返回;
    //若不为0,则进入AQS的队列等待
    if (tryAcquireShared(arg) < 0)
        doAcquireSharedInterruptibly(arg);
}

//sync类实现的AQS的接口
protected int tryAcquireShared(int acquires) {
    return (getState() == 0) ? 1 : -1;
}

As seen from the code feature of the process can be interrupted thread access to resources, and the resource is a shared resource acquired.

acquireSharedInterruptibly () method first determine whether the current thread has been interrupted, if an exception is thrown, or call sync achieve tryAcquireShared () method to view the current state value (counter value) is 0, then the current thread is the await () method returns otherwise, call the AQS doAcquireSharedInterruptibly () method to let the current thread blocked. Further can be seen that, where arg parameter passed tryAcquireShared not been used, the method is only invoked tryAcquireShared order to check the current state value is not 0, and so is not called CAS current state value minus 1.

boolean await(long timeout, TimeUnit unit)方法

When the thread calls this method CountDownLatch object, the current thread will be blocked until one of the following happens will return:

  • When all threads call a method countDown CountDownLatch object, that is, when the counter value is 0, this time will return true;
  • to set the timeout time because the timeout return false;
  • Other thread calls interrupt the current thread () method interrupts the current thread, the current thread will throw InterruptedException exception and then return.
public boolean await(long timeout, TimeUnit unit)
        throws InterruptedException {
    return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}

public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    return tryAcquireShared(arg) >= 0 ||
        doAcquireSharedNanos(arg, nanosTimeout);
}

void countDown () method

After thread calls this method, the value of the counter is decremented, after decreasing the counter value is 0 if the wake-up call because all await method is blocked thread, otherwise do nothing.

The following look countDown () method is a method of how to call AQS:

public void countDown() {
    //委托sync调用AQS的方法
    sync.releaseShared(1);
}

//AQS的方法
public final boolean releaseShared(int arg) {
    //调用sync实现的tryReleaseShared
    if (tryReleaseShared(arg)) {
        //AQS的释放资源方法
        doReleaseShared();
        return true;
    }
    return false;
}

In the above code, releaseShared first call tryReleaseShared method of AQS sync implementation.

Code is as follows:

//sync的方法
protected boolean tryReleaseShared(int releases) {
    //循环进行CAS,直到当前线程成功完成CAS使计数器值(状态值state)减1并更新到state
    for (;;) {
        int c = getState();
        //如果当前状态值为0则直接返回 (1)
        if (c == 0)
            return false;
        //使用CAS让计数器值减1 (2)
        int nextc = c-1;
        if (compareAndSetState(c, nextc))
            return nextc == 0;
    }
}

Code above first obtains the current state value (counter value) Code (1) determines if the current state is 0 directly returns false, so the countDown () method returns; otherwise execute code (2) using the counter value by one CAS, CAS fails retry cycle, otherwise it returns true if the current counter value is 0, returns true explanation is
the final countdown thread calls the method, then the thread in addition to the counter value by one, but also need to wake await method calls because of CountDownLatch the thread is blocked, the specific method is to call doReleaseShared AQS to activate the blocked thread.

Here Code (1) seemingly superfluous, they are not true, the reason for adding code (1) is to prevent when the counter value is 0, and the other thread calls countDown method, if no code (1), the state value may be become negative.

Note: If the internal counter CountDownLatch error because the program never reaches zero, then wait for the thread on the corresponding instance would have been in the WAITING state .

Avoid this problem in two ways:

  • Ensure that all CountDownLatch.countDown () calls the code located in the correct position, such as the finally block
  • Use CountDownLatch.await (long, TimeUnit) this method, the waiting thread to specify a time limit, if the timeout, the waiting thread will automatically wake up.

long getCount () method

Get the current value of the counter is below the value of the state of the AQS look code:

public long getCount() {
    return sync.getCount();
}

int getCount() {
    return getState();
}

As seen from the code, or calling the inside of AQS getState method to get the value of the state (current counter value).

summary

CountDownLatch use AQS bottom is achieved. AQS state variables used to store counter values. First, setting the state value (counter value) during initialization CountDownLatch, when a plurality of threads is actually calling method countDown AQS decreasing atomic state value. When the thread invoked await the current thread will be placed in a queue waiting for AQS blocking counter to zero and back again. Other thread calls countDown way for the counter value is decremented l, when the counter value becomes zero, the current thread should call doReleaseShared method of AQS activated by a call to await () method is blocked thread.

Published 21 original articles · won praise 8 · views 886

Guess you like

Origin blog.csdn.net/kaihuishang666/article/details/103989428