一:CountdownLatch
1:基本含义
CountDownLatch中count down是倒数的意思,latch则是门闩的含义。整体含义可以理解为倒数的门栓,似乎有一点“三二一,芝麻开门”的感觉。CountDownLatch的作用也是如此,==在构造CountDownLatch的时候需要传入一个整数n,在这个整数“倒数”到0之前,主线程需要等待在门口,而这个“倒数”过程则是由各个执行线程驱动的,每个线程执行完一个任务“倒数”一次。
总结来说,CountDownLatch的作用就是等待其他的线程都执行完任务,必要时可以对各个任务的执行结果进行汇总,然后主线程才继续往下执行。
2:常用方法
- countDown()和await()。countDown()方法用于使计数器减一,其一般是执行任务的线程调用,await()方法则使调用该方法的线程处于等待状态,其一般是主线程调用。
- 这里需要注意的是,countDown()方法并没有规定一个线程只能调用一次,当同一个线程调用多次countDown()方法时,每次都会使计数器减一;
- 另外,await()方法也并没有规定只能有一个线程执行该方法,如果多个线程同时执行await()方法,那么这几个线程都将处于等待状态,并且以共享模式享有同一个锁。
- 共享锁:是指该锁可被多个线程所持有,允许多个线程同时去获取。
二:主要方法解析
1:countDown()
public void countDown() {sync.releaseShared(1);
}
2:AQS中的releaseShared(int arg)
(1):releaseShared(int arg)
public final boolean releaseShared(int arg) {if (tryReleaseShared(arg)) {doReleaseShared();return true;}return false;}
(2):tryReleaseShared(int releases)
protected boolean tryReleaseShared(int releases) {// Decrement count; signal when transition to zerofor (;;) {int c = getState();if (c == 0)//如果其计数器就是0的话,那就不用去唤醒其它线程了,直接执行本线程,所以返回falsereturn false;int nextc = c-1;if (compareAndSetState(c, nextc))return nextc == 0;//如果我们的计数器为0了的话,那么就返回true 就可以执行 唤醒其他线程的操作了}}
(3): doReleaseShared();(唤醒其它线程)
private void doReleaseShared() {/** Ensure that a release propagates, even if there are other* in-progress acquires/releases. This proceeds in the usual* way of trying to unparkSuccessor of head if it needs* signal. But if it does not, status is set to PROPAGATE to* ensure that upon release, propagation continues.* Additionally, we must loop in case a new node is added* while we are doing this. Also, unlike other uses of* unparkSuccessor, we need to know if CAS to reset status* fails, if so rechecking.*/for (;;) {Node h = head;if (h != null && h != tail) {int ws = h.waitStatus;if (ws == Node.SIGNAL) {if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))continue; // loop to recheck casesunparkSuccessor(h);}else if (ws == 0 &&!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))continue; // loop on failed CAS}if (h == head) // loop if head changedbreak;}}
- 在执行sync.releaseShared(1)方法时,其在调用tryReleaseShared(int)方法时会在无限for循环中设置state属性的值,设置成功之后其会根据设置的返回值(此时state已经自减了一),即当前线程是否为将state属性设置为0的线程,来判断是否执行if块中的代码。
- doReleaseShared()方法主要作用是唤醒调用了await()方法的线程。需要注意的是,如果有多个线程调用了await()方法,这些线程都是以共享的方式等待被唤醒的,试想,如果以独占的方式等待,那么当计数器减少至零时,就只有一个线程会被唤醒执行await()之后的代码,这显然不符合逻辑。
三:上码应用
1:简单认识CountDownLatch的线程并行
我们定义一个线程池,其可以开启4个线程,我们再定义CountDownLatch(3),也就是一次可以让三个线程并行的的执行,只有这三次线程并行的执行完了之后,才可以让第四个线程执行。
public class TextCountDownLatch {public static void main(String[] args) {CountDownLatch downLatch = new CountDownLatch(3);ExecutorService executorService = Executors.newFixedThreadPool(4);executorService.submit(()->{System.out.println(Thread.currentThread().getName()+"...begin");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}downLatch.countDown();System.out.println(Thread.currentThread().getName()+"...end...." + downLatch.getCount());});executorService.submit(()->{System.out.println(Thread.currentThread().getName()+"...begin");try {Thread.sleep(1500);} catch (InterruptedException e) {e.printStackTrace();}downLatch.countDown();System.out.println(Thread.currentThread().getName()+"...end...." + downLatch.getCount() );});executorService.submit(()->{System.out.println(Thread.currentThread().getName()+"...begin");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}downLatch.countDown();System.out.println(Thread.currentThread().getName()+"...end...."+downLatch.getCount());});executorService.submit(()->{try {System.out.println(Thread.currentThread().getName()+"...waiting");downLatch.await();System.out.println(Thread.currentThread().getName()+"...start");} catch (InterruptedException e) {e.printStackTrace();}});}
}
2:CountDownLatch的应用
(1):前言
在我们打开游戏的时候,往往不是直接进入的游戏的,而是先等游戏加载完,然后再进行进入。然而这个加载的过程就是并行的执行多条线程。
(2):上码
public static void main(String[] args) throws InterruptedException {ExecutorService service = Executors.newFixedThreadPool(10);CountDownLatch latch = new CountDownLatch(10);String[] all = new String[10];Random random = new Random();for (int j = 0; j < 10; j++) {int k = j;service.submit(()->{for (int i = 0; i <= 100 ; i++) {try {Thread.sleep(random.nextInt(100));} catch (InterruptedException e) {e.printStackTrace();}all[k] = i +"%";System.out.print("\r"+Arrays.toString(all));}latch.countDown();//每加载完一个线程的话,那么就计数减一,});}latch.await();//等待那十个线程结束后唤醒System.out.println("/n"+"游戏开始");service.shutdown();//线程池关闭}