一慌烧、CountDownLatch基本原理
- countDownLatch最基本的原理其實(shí)就是逐抑,現(xiàn)在有4個(gè)客戶端鸠儿,分別是A、B厕氨、C进每、D,客戶端A進(jìn)行加鎖后,設(shè)置三個(gè)線程來(lái)獲取鎖命斧,那么田晚,必須讓接下來(lái)的三個(gè)客戶端BCD都獲取鎖成功后,客戶端A的邏輯才會(huì)繼續(xù)向下走
- 如果說(shuō)国葬,指定3個(gè)客戶端獲取鎖贤徒,獲取鎖的客戶端數(shù)量沒(méi)有到達(dá)3的話芹壕,客戶端A是不會(huì)邏輯是不會(huì)向下走的,會(huì)被阻塞住
源碼
代碼片段一接奈、demo
public static void main(String[] args) throws Exception {
Config config = new Config();
config.useClusterServers()
.addNodeAddress("redis://192.168.0.107:7001")
.addNodeAddress("redis://192.168.0.107:7002")
.addNodeAddress("redis://192.168.0.110:7003")
.addNodeAddress("redis://192.168.0.110:7004")
.addNodeAddress("redis://192.168.0.111:7005")
.addNodeAddress("redis://192.168.0.111:7006");
final RedissonClient redisson = Redisson.create(config);
RCountDownLatch latch = redisson.getCountDownLatch("anyCountDownLatch");
// 這里會(huì)設(shè)置幾個(gè)客戶端來(lái)獲取鎖成功的數(shù)量,代碼片段二踢涌、
latch.trySetCount(3);
System.out.println(new Date() + ":線程[" + Thread.currentThread().getName() + "]設(shè)置了必須有3個(gè)線程執(zhí)行countDown,進(jìn)入等待中序宦。睁壁。。");
for (int i = 0; i < 3; i++) {
new Thread(new Runnable() {
public void run() {
try {
System.out.println(new Date() + ":線程[" + Thread.currentThread().getName() + "]在做一些操作互捌,請(qǐng)耐心等待潘明。。秕噪。钳降。。腌巾。");
Thread.sleep(3000);
RCountDownLatch localLatch = redisson.getCountDownLatch("anyCountDownLatch”);
// 代碼片段三牲阁、
localLatch.countDown();
System.out.println(new Date() + ":線程[" + Thread.currentThread().getName() + "]執(zhí)行countDown操作");
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
// 代碼片段四、
latch.await();
System.out.println(new Date() + ":線程[" + Thread.currentThread().getName() + "]收到通知壤躲,有3個(gè)線程都執(zhí)行了countDown操作城菊,可以繼續(xù)往下走");
}
代碼片段二、RedissonCountDownLatch
- 參數(shù):
KEYS[1]= “anyCountDownLatch”
ARGV[2] = 3,其實(shí)就是count參數(shù)的值
@Override
public boolean trySetCount(long count) {
// 這里設(shè)置的客戶端獲取鎖的個(gè)數(shù)為3
return get(trySetCountAsync(count));
}
// count = 3
@Override
public RFuture<Boolean> trySetCountAsync(long count) {
return commandExecutor.evalWriteAsync(getName(),
LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
//1.exists anyCountDownLatch 客戶端A進(jìn)來(lái)不存在碉克,
// 進(jìn)入if邏輯
"if redis.call('exists', KEYS[1]) == 0 then “
//1.set anyCountDownLatch 3
+ "redis.call('set', KEYS[1], ARGV[2]); "
+ "redis.call('publish', KEYS[2], ARGV[1]); “
//1.返回1代表成功
+ "return 1 "
+ "else "
+ "return 0 "
+ "end",
Arrays.<Object>asList(getName(), getChannelName()),
newCountMessage, count);
}
代碼片段三凌唬、RedissonCountDownLatch
參數(shù)
KEYS[1] = “anyCountDownLatch”
@Override
public void countDown() {
get(countDownAsync());
}
@Override
public RFuture<Void> countDownAsync() {
return commandExecutor.evalWriteAsync(getName(),
LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
//1.decr anyCountDownLatch ,其實(shí)就是anyCountDownLatch這KEY對(duì)應(yīng)的值,
// 第一次是3漏麦,減去1客税,變成2,
// 這樣的話撕贞,后面還允許2個(gè)客戶端獲取鎖
"local v = redis.call('decr', KEYS[1]);” +
//1.如果v更耻,第一次執(zhí)行后為2小于等于0的話,就直接刪除key捏膨,所以可以看到秧均,
// 當(dāng)執(zhí)行到第三個(gè)客戶端的時(shí)候,這里才會(huì)成立
"if v <= 0 then redis.call('del', KEYS[1]) end;" +
"if v == 0 then redis.call('publish', KEYS[2], ARGV[1]) end;",
Arrays.<Object>asList(getName(), getChannelName()), zeroCountMessage);
}
代碼片段四号涯、RedissonCountDownLatch
public void await() throws InterruptedException {
RFuture<RedissonCountDownLatchEntry> future = subscribe();
try {
commandExecutor.syncSubscription(future);
// 這里就是說(shuō)目胡,如果我們業(yè)務(wù)邏輯里設(shè)置的成功獲取鎖的客戶端為3,如果三個(gè)客戶端都已經(jīng)成功獲取鎖链快,
//那么KEY就不存在了,如代碼片段三中的分析誉己,而如果獲取鎖的客戶端沒(méi)有達(dá)到3的話,這里其實(shí)就會(huì)進(jìn)入到一個(gè)死循環(huán)
// 不停的等待,直到KEY的值為0域蜗,參會(huì)繼續(xù)走下面的邏輯巨双,否則就會(huì)一直阻塞在這里
while (getCount() > 0) {
// waiting for open state
RedissonCountDownLatchEntry entry = getEntry();
if (entry != null) {
entry.getLatch().await();
}
}
} finally {
unsubscribe(future);
}
}
三噪猾、demo執(zhí)行結(jié)果圖
- 剛開(kāi)始main線程設(shè)置必須有三個(gè)線程/客戶端執(zhí)行countDown,也就是獲取鎖成功
- 接下來(lái)三個(gè)線程/客戶端成功的獲取了鎖
- 最后三個(gè)線程獲取鎖執(zhí)行,執(zhí)行countDown邏輯后筑累,主線程才會(huì)繼續(xù)執(zhí)行畏妖,否則就會(huì)一直阻塞住
四、總結(jié)
- 到此為止疼阔,Redisson的源碼基本上已經(jīng)分析差不多了戒劫,其實(shí)還有一些環(huán)境沒(méi)有發(fā)出來(lái),因?yàn)橐黄恼虏幌胩L(zhǎng)婆廊,所以后面會(huì)陸陸續(xù)續(xù)的把redisson源碼的其他部分發(fā)出來(lái)
- 接下來(lái)就要分析zk的分布式鎖了迅细,其實(shí)對(duì)比Redis分布式鎖和zk的分布式鎖的優(yōu)缺點(diǎn),全方面的進(jìn)行對(duì)比之后淘邻,在我們的實(shí)際業(yè)務(wù)開(kāi)發(fā)中茵典,我們才會(huì)知道,到底哪個(gè)分布式鎖更加適合我們宾舅,因?yàn)橥嘲ⅲ瑳](méi)有最好,只有最合適筹我。
- 大家一起努力扶平,加油。