预见猿份
主题
首页面试题模拟面试SQL练习在线工具关于我们老苗一对一私教学员评价
实战项目
零基础学习路线项目前置基础创新WMS项目Java微服务框架与实战云岚到家项目闪聚支付项目学成在线项目青橙电商项目JVM原理与实战调优分布式事务专题Java高频面试题MySQL从入门到精通达梦数据库从入门到实战MongoDB从入门到实战PostgreSQL从入门到实战TiDB从入门到实战Java数据结构与算法Java 并发编程(JUC)实战课程老苗一对一私教学员评价
blog我的会员
  • Java 并发编程(JUC)实战课程

    • 课程介绍
    • 01 多线程入门
    • 02 线程的常用方法
    • 03 线程池
    • 04 synchronized教程
    • 05 ReentrantLock教程
    • 06 读写锁
    • 07 JMM教程
    • 08 CAS教程
    • 09 wait&notify
    • 10 常用的并发集合
    • 11 异步任务控制
    • 12 常用并发工具







----- 到底线了 -----

第12章 常用并发工具 ​

4.1 CountDownLatch ​

4.1.1 CountDownLatch介绍 ​

CountDownLatch是一种倒计时控制多线程的并发工具类,比如下边的例子:线程1需要在线程2和线程3完成工作后才可以继续执行,流程如下:

配图25

1、首先设定计数器(例子: new CountDownLatch(2)表示计数器为2)。

2、线程thread01调用awit()方法等待计算器清零。

3、线程thread02调用CountDownLatch的countDown()方法将计数器减1.

4、线程thread03调用CountDownLatch的countDown()方法将计数器减1.

5、计数器清零唤醒线程thread01继续执行。

CountDownLatch可以实现一个线程等多个线程、多个线程等一个线程,它的应用场景有很多,比如下边的社保查询例子:

配图26

分别用不同的线程查询居民信息和参保信息等信息,最后将每个信息进行汇总组装。

CountDownLatch的Api方法如下:

配图27

4.1.2 CountDownLatch测试 ​

下边模拟两个线程查询参保状态、交费状态,另外一个线程进行信息组装,代码如下:

java
package com.yjoffer.javase.thread.waitnotify;


import java.util.concurrent.CountDownLatch;  
import java.util.concurrent.ExecutorService;  
import java.util.concurrent.Executors;

import java.util.concurrent.TimeUnit;

import javax.xml.bind.helpers.ValidationEventImpl;

/**

- 并发工具测试
- @author 预见猿份(www.yjoffer.com)
- 

*/  
public class ThreadUtilsTest {


//测试CountDownLatch
public static void test_CountDownLatch() {
	//计数器为2
	CountDownLatch countDownLatch = new CountDownLatch(2);
	ExecutorService threadPool = Executors.newCachedThreadPool();
	threadPool.execute(()->{
		logger.debug("组装线程等待中...");
		try {
			countDownLatch.await();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		logger.debug("组装完成");
	});
	threadPool.execute(()->{
		try {
			TimeUnit.SECONDS.sleep(2);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		logger.debug("参保状态");
		//计数器减1
		countDownLatch.countDown();
	});
	threadPool.execute(()->{
		try {
			TimeUnit.SECONDS.sleep(1);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		logger.debug("交费状态");
		//计数器减1
		countDownLatch.countDown();
	});

}

//休眠
public static void sleep(int time,TimeUnit timeUnit) {
	try {
		timeUnit.sleep(time);
	} catch (InterruptedException e) {
		e.printStackTrace();
	}
}
public static void main(String[] args) {
	test_CountDownLatch1();
}


}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

输出:

java
21:52:43.491 FINE com.yjoffer.javase.thread2.waitnotify.ThreadUtilsTest[pool-1-thread-1] - 组装线程等待中...
21:52:44.492 FINE com.yjoffer.javase.thread2.waitnotify.ThreadUtilsTest[pool-1-thread-3] - 交费状态
21:52:45.491 FINE com.yjoffer.javase.thread2.waitnotify.ThreadUtilsTest[pool-1-thread-2] - 参保状态
21:52:45.492 FINE com.yjoffer.javase.thread2.waitnotify.ThreadUtilsTest[pool-1-thread-1] - 组装完成
1
2
3
4

4.1.3 CountDownLatch工作原理 ​

查看CountDownLatch类的源代码:

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

sync类继承了AbstractQueuedSynchronizer(AQS类),源代码如下:

java
private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;

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

        int getCount() {
            return getState();
        }

        protected int tryAcquireShared(int acquires) {
            return (getState() == 0) ? 1 : -1;
        }

        protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

AQS是ReentrantLock、CountDownLatch等工具类的底层类,我们可以查一下ReentrantLock的源代码,如下:

配图28

AQS字面意思是“抽象队列同步器”,基于AQS自定义同步器可以实现独占锁、共享锁及并发工具。

AQS的工作原理的核心是共享状态state和先进先出双向队列,共享状态state作为多线程访问的共享变量,通过它可以控制锁的获取与释放,FIFO双向队列用于存放等待获取锁的线程,相当于线程的阻塞队列,下边通过一个同步锁的例子说明它的工作过程。如下图:

配图29

1、线程1获取锁,使用CAS将state由0更新为1成功,线程1即Node1作为队列的头结点。

2、线程2获取锁,使用CAS将state由0更新为1失败,线程2即Node2加入队尾等待锁的释放。

3、线程1释放锁,使用CAS将state由1更新为0成功,出队,唤醒它的后继结点Node2,线程2获取锁成功。

4、线程3去获取锁,加入队尾,等待线程2释放锁后被唤醒去获取锁。

CountDownLatch使用的是AQS的共享锁模式。

继续查看await()方法的源代码,最终找到Sync类的获取锁的方法:

java
        protected int tryAcquireShared(int acquires) {
            return (getState() == 0) ? 1 : -1;
        }
1
2
3

state状态为0表示锁空闲可获取锁,否则不允许获取,tryAcquireShared方法返回正数表示获取成功,负数表示失败。

在设定CountDownLatch计数器时指定count,源代码如下:

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

设定义的count即是state状态的初始值。

只有当state为0才表示计数清零,await线程才可以继续执行,否则进入阻塞队列阻塞。

如何让state清零呢?

其它线程执行countDown()方法表示state减1,源代码如下:

java
    public void countDown() {
        sync.releaseShared(1);
    }

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

    protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

在tryReleaseShared方法中找到答案,使用cas让state减1。

如果state等于0则tryReleaseShared方法返回true,唤醒等待的线程。

state等于0再次调用await()方法将不再阻塞。

🔒关注公众号,免费阅读全文

请免费关注 预见猿份 公众号,发送:yjoffer.com,获取解锁密码

公众号二维码微信扫码关注

关注公众号回复获取密码后,在下方输入并点击解锁,本机后续再读无需重复输入。

← 11 异步任务控制








如果发现文档内容有错误或排版错乱,请及时联系站长老苗修改,不胜感激。联系我们
关于我们 | 隐私政策 | 豫ICP备2026003386号-4 | 豫公网安备41010202004008号
目录

本页无章节