1. 河豚號 > 生活百科 >

java技術(shù)分享ppt干貨(java基礎(chǔ)知識點整理)

今天介紹基于ZooKeeper的分布式鎖的簡單實現(xiàn),包括阻塞鎖和非阻塞鎖。同時增加了網(wǎng)上很少介紹的基于節(jié)點的非阻塞鎖實現(xiàn),主要是為了加深對ZooKeeper的理解。

 

Java技術(shù)分享:基于ZooKeeper的三種分布式鎖實現(xiàn)

 

維基百科:分布式鎖,是控制分布式系統(tǒng)之間同步訪問共享資源的一種方式。在分布式系統(tǒng)中,常常需要協(xié)調(diào)他們的動作。如果不同的系統(tǒng)或是同一個系統(tǒng)的不同主機之間共享了一個或一組資源,那么訪問這些資源的時候,往往需要互斥來防止彼此干擾來保證一致性,在這種情況下,便需要使用到分布式鎖。

1 阻塞鎖和非阻塞鎖

根據(jù)業(yè)務(wù)特點,普通分布式鎖有兩種需求:阻塞鎖和非阻塞鎖。

 

Java技術(shù)分享:基于ZooKeeper的三種分布式鎖實現(xiàn)

 

阻塞鎖:多個系統(tǒng)同時調(diào)用同一個資源,所有請求被排隊處理。已經(jīng)得到分布式鎖的系統(tǒng),進入運行狀態(tài)完成業(yè)務(wù)操作;沒有得到分布式鎖的線程進入阻塞狀態(tài)等待,當(dāng)獲得相應(yīng)的信號并獲得分布式鎖后,進入運行狀態(tài)完成業(yè)務(wù)操作。

 

Java技術(shù)分享:基于ZooKeeper的三種分布式鎖實現(xiàn)

 

非阻塞鎖:多個系統(tǒng)同時調(diào)用同一個資源,當(dāng)某一個系統(tǒng)最先獲取到鎖,進入運行狀態(tài)完成業(yè)務(wù)操作;其他沒有得到分布式鎖的系統(tǒng),就直接返回,不做任何業(yè)務(wù)邏輯,可以給用戶提示進行其他操作。

 

Java技術(shù)分享:基于ZooKeeper的三種分布式鎖實現(xiàn)

 

2 鎖代碼簡單設(shè)計

基于ZooKeeper實現(xiàn)鎖,一般都是創(chuàng)建EPHEMERAL_SEQUENTIAL子節(jié)點并比較序號實現(xiàn)的。參照Redis的分布式鎖實現(xiàn),也可以使用EPHEMERAL節(jié)點實現(xiàn)。

 

Java技術(shù)分享:基于ZooKeeper的三種分布式鎖實現(xiàn)

 

3 分布式鎖代碼

完整代碼比較多,占篇幅。在文中只保留了關(guān)鍵的代碼。完整項目代碼放到了github(

https://github.com/SeemSilly/codestory/tree/master/research-zoo-keeper ),感興趣的可以關(guān)注。

3.1 分布式鎖接口定義

ZooKeeperLock.java

public interface ZooKeeperLock {

/**

* 嘗試獲取鎖

*

* @param guidNodeName 用于加鎖的唯一節(jié)點名

* @param clientGuid 用于唯一標(biāo)識當(dāng)前客戶端的ID

* @return

*/

boolean lock(String guidNodeName, String clientGuid);

/**

* 釋放鎖

*

* @param guidNodeName 用于加鎖的唯一節(jié)點名

* @param clientGuid 用于唯一標(biāo)識當(dāng)前客戶端的ID

* @return

*/

boolean release(String guidNodeName, String clientGuid);

/**

* 鎖是否已經(jīng)存在

*

* @param guidNodeName 用于加鎖的唯一節(jié)點名

* @return

*/

boolean exists(String guidNodeName);

}

3.2 基于節(jié)點實現(xiàn)的非阻塞鎖

NodeBlocklessLock.java

public class NodeBlocklessLock extends ZooKeeperBase implements ZooKeeperLock {

/** 嘗試獲取鎖 */

public boolean lock(String guidNodeName, String clientGuid) {

boolean result = false;

if (getZooKeeper().exists(guidNodeName, false) == null) {

getZooKeeper().create(guidNodeName, clientGuid.getBytes(),

ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);

byte[] data = getZooKeeper().getData(guidNodeName, false, null);

if (data != null && clientGuid.equals(new String(data))) {

result = true;

}

}

return result;

}

/** 釋放鎖 */

public boolean release(String guidNodeName, String clientGuid) {

boolean result = false;

Stat stat = new Stat();

byte[] data = getZooKeeper().getData(guidNodeName, false, stat);

if (data != null && clientGuid.equals(new String(data))) {

getZooKeeper().delete(guidNodeName, stat.getVersion());

result = true;

}

return result;

}

/** 鎖是否已經(jīng)存在 */

public boolean exists(String guidNodeName) {

boolean result = false;

Stat stat = getZooKeeper().exists(guidNodeName, false);

result = stat != null;

return result;

}

}

3.3 基于子節(jié)點實現(xiàn)的分布式鎖基類

ChildrenNodeLock.java

public abstract class ChildrenNodeLock extends ZooKeeperBase implements ZooKeeperLock {

/** 獲取當(dāng)前節(jié)點的前一個節(jié)點,如果為空表示自己是第一個 */

protected String getPrevElementName() {

List elementNames = getZooKeeper().getChildren(this.guidNodeName, false);

long curElementSerial = Long.valueOf(

elementNodeFullName.substring((this.guidNodeName + “/” + childPrefix).length()));

String prevElementName = null;

long prevElementSerial = -1;

for (String oneElementName : elementNames) {

long oneElementSerial = Long.parseLong(oneElementName.substring(childPrefix.length()));

if (oneElementSerial < curElementSerial) {

// 比當(dāng)前節(jié)點小

if (oneElementSerial > prevElementSerial) {

prevElementSerial = oneElementSerial;

prevElementName = oneElementName;

}

}

}

return prevElementName;

}

/** 嘗試獲取鎖 */

public boolean lock(String guidNodeName, String clientGuid) {

boolean result = false;

// 確保根節(jié)點存在,并且創(chuàng)建為容器節(jié)點

super.createRootNode(this.guidNodeName, CreateMode.CONTAINER);

// 創(chuàng)建子節(jié)點并返回帶序列號的節(jié)點名

elementNodeFullName = getZooKeeper().create(this.guidNodeName + “/” + childPrefix,

new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);

boolean lockSuccess = isLockSuccess();

result = lockSuccess;

return result;

}

/** 釋放鎖 */

public boolean release(String guidNodeName, String clientGuid) {

// 刪除子節(jié)點

getZooKeeper().delete(elementNodeFullName, 0);

return true;

}

/** 鎖是否已經(jīng)存在,容器節(jié)點存在,并且有子節(jié)點,則說明鎖已經(jīng)存在 */

public boolean exists(String guidNodeName) {

boolean exists = false;

Stat stat = new Stat();

try {

getZooKeeper().getData(guidNodeName, false, stat);

exists = stat.getNumChildren() > 0;

} catch (KeeperException.NoNodeException e) {

exists = false;

}

return exists;

}

/** 是否加鎖成功 , 由子類實現(xiàn) */

protected abstract boolean isLockSuccess();

}

3.4 基于子節(jié)點實現(xiàn)的非阻塞鎖

ChildrenBlocklessLock.java

public class ChildrenBlocklessLock extends ChildrenNodeLock {

/** 是否加鎖成功 */

protected boolean isLockSuccess() throws KeeperException, InterruptedException {

boolean lockSuccess = false;

String prevElementName = getPrevElementName();

if (prevElementName != null) {

// 有更小的節(jié)點,說明當(dāng)前節(jié)點沒搶到鎖,刪掉自己并退出

getZooKeeper().delete(elementNodeFullName, 0);

} else {

lockSuccess = true;

}

return lockSuccess;

}

}

3.5 基于子節(jié)點實現(xiàn)的阻塞鎖

ChildrenBlockingLock.java

public class ChildrenBlockingLock extends ChildrenNodeLock {

/** 前一個節(jié)點被刪除的信號 */

static Integer mutex = Integer.valueOf(-1);

/** 監(jiān)控的節(jié)點被刪除 */

protected void processNodeDeleted(WatchedEvent event) {

synchronized (mutex) {

// 節(jié)點被刪除,通知退出線程

mutex.notify();

}

}

/** 是否加鎖成功 */

protected boolean isLockSuccess() {

boolean lockSuccess;

while (true) {

String prevElementName = getPrevElementName();

if (prevElementName == null) {

lockSuccess = true;

break;

} else {

// 有更小的節(jié)點,說明當(dāng)前節(jié)點沒搶到鎖,注冊前一個節(jié)點的監(jiān)聽

getZooKeeper().exists(this.guidNodeName + “/” + prevElementName, true);

synchronized (mutex) {

mutex.wait();

log.info(“{} 被刪除,看看是不是輪到自己了”, prevElementName);

}

}

}

return lockSuccess;

}

}

4 測試用例

4.1 測試代碼

LockClientThread.java 獲取分布式鎖和釋放鎖

public class LockClientThread extends Thread {

/** 模擬獲取分布式鎖,成功后執(zhí)行業(yè)務(wù) */

public void run() {

boolean locked = zooKeeperLock.lock(guidNodeName, clientGuid);

if (locked) {

log.info(“{} lock() success,拿到鎖了,假裝忙2秒”, clientGuid);

Thread.sleep(2000);

boolean released = zooKeeperLock.release(guidNodeName, clientGuid);

log.info(“{} release() result : {}”, clientGuid, released);

} else {

log.info(“{} lock() fail”, clientGuid);

}

}

}

模擬多個客戶端并發(fā)執(zhí)行

public void testChildrenBlocklessMultiThread() throws IOException {

String guidNodeName = “/multi-” + System.currentTimeMillis();

int threadCount = 5;

LockClientThread[] threads = new LockClientThread[threadCount];

for (int i = 0; i < threadCount; i++) {

ChildrenBlocklessLock nodeBlocklessLock = new ChildrenBlocklessLock(address);

threads[i] = new LockClientThread(nodeBlocklessLock, guidNodeName, “client-” + (i + 1));

}

for (int i = 0; i < threadCount; i++) {

threads[i].start();

}

}

4.2 非阻塞鎖的測試結(jié)果

可以看到,只有一個線程能搶到鎖并執(zhí)行業(yè)務(wù),其他線程都直接退出。

55:43.929 [INFO] LockClientThread.run(33) client-1 lock() …

55:43.942 [INFO] LockClientThread.run(33) client-3 lock() …

55:43.947 [INFO] LockClientThread.run(33) client-2 lock() …

55:43.948 [INFO] LockClientThread.run(33) client-4 lock() …

55:43.949 [INFO] LockClientThread.run(33) client-5 lock() …

55:44.052 [INFO] LockClientThread.run(36) client-1 lock() success,拿到鎖了,假裝忙2秒

55:44.072 [INFO] LockClientThread.run(47) client-5 lock() fail

55:44.085 [INFO] LockClientThread.run(47) client-4 lock() fail

55:44.091 [INFO] LockClientThread.run(47) client-2 lock() fail

55:44.096 [INFO] LockClientThread.run(47) client-3 lock() fail

55:46.053 [INFO] LockClientThread.run(42) client-1 release() …

55:46.057 [INFO] LockClientThread.run(44) client-1 release() result : true

4.3 阻塞鎖的測試結(jié)果

可以看到,搶到分布式鎖的線程執(zhí)行業(yè)務(wù),沒搶到鎖的線程會等到直到鎖被釋放重新獲取到鎖后再執(zhí)行業(yè)務(wù)。

 

Java技術(shù)分享:基于ZooKeeper的三種分布式鎖實現(xiàn)

 

59:32.802 [INFO] LockClientThread.run(33) client-1 lock() …

59:32.811 [INFO] LockClientThread.run(33) client-3 lock() …

59:32.812 [INFO] LockClientThread.run(33) client-4 lock() …

59:32.813 [INFO] LockClientThread.run(33) client-2 lock() …

59:32.813 [INFO] LockClientThread.run(33) client-5 lock() …

59:32.836 [INFO] LockClientThread.run(36) client-1 lock() success,拿到鎖了,假裝忙2秒

59:34.836 [INFO] LockClientThread.run(42) client-1 release() …

59:34.844 [INFO] LockClientThread.run(44) client-1 release() result : true

59:34.846 [INFO]

ChildrenBlockingLock.isLockSuccess(55) element0000000000 被刪除,看看是不是輪到自己了

59:34.848 [INFO] LockClientThread.run(36) client-5 lock() success,拿到鎖了,假裝忙2秒

59:36.848 [INFO] LockClientThread.run(42) client-5 release() …

59:36.852 [INFO]

ChildrenBlockingLock.isLockSuccess(55) element0000000001 被刪除,看看是不是輪到自己了

59:36.852 [INFO] LockClientThread.run(44) client-5 release() result : true

59:36.855 [INFO] LockClientThread.run(36) client-2 lock() success,拿到鎖了,假裝忙2秒

59:38.855 [INFO] LockClientThread.run(42) client-2 release() …

59:38.869 [INFO]

ChildrenBlockingLock.isLockSuccess(55) element0000000002 被刪除,看看是不是輪到自己了

59:38.870 [INFO] LockClientThread.run(44) client-2 release() result : true

59:38.876 [INFO] LockClientThread.run(36) client-4 lock() success,拿到鎖了,假裝忙2秒

59:40.877 [INFO] LockClientThread.run(42) client-4 release() …

59:40.881 [INFO]

ChildrenBlockingLock.isLockSuccess(55) element0000000003 被刪除,看看是不是輪到自己了

59:40.882 [INFO] LockClientThread.run(44) client-4 release() result : true

59:40.884 [INFO] LockClientThread.run(36) client-3 lock() success,拿到鎖了,假裝忙2秒

59:42.884 [INFO] LockClientThread.run(42) client-3 release() …

59:42.887 [INFO] LockClientThread.run(44) client-3 release() result : true

end:如果你覺得本文對你有幫助的話,記得關(guān)注點贊轉(zhuǎn)發(fā),你的支持就是我更新動力。

本文由網(wǎng)上采集發(fā)布,不代表我們立場,轉(zhuǎn)載聯(lián)系作者并注明出處:http://m.zltfw.cn/shbk/39361.html

聯(lián)系我們

在線咨詢:點擊這里給我發(fā)消息

微信號:15705946153

工作日:9:30-18:30,節(jié)假日休息