├── README.md
├── src
├── main
│ └── java
│ │ └── com
│ │ └── titizz
│ │ └── exercise
│ │ └── distributedlock
│ │ ├── LockStatus.java
│ │ ├── ReadWriteLock.java
│ │ ├── DistributedLock.java
│ │ ├── ExclusiveLock.java
│ │ ├── ZKReadWriteLock2.java
│ │ └── ZKReadWriteLock1.java
└── test
│ └── java
│ └── com
│ └── titizz
│ └── exercise
│ └── distributedlock
│ ├── ZKWriteLockTest2.java
│ ├── ExclusiveLockTest.java
│ ├── ZKReadLockTest2.java
│ ├── ZKReadLockTest1.java
│ └── ZKWriteLockTest1.java
├── .gitignore
└── pom.xml
/README.md:
--------------------------------------------------------------------------------
1 | # distributed_lock
2 | 基于 Zookeeper 实现的分布式锁,包含独占锁和读写锁两种实现。详细参见我的博文:[基于Zookeeper的分布式锁实现](https://segmentfault.com/a/1190000010895869)
3 |
--------------------------------------------------------------------------------
/src/main/java/com/titizz/exercise/distributedlock/LockStatus.java:
--------------------------------------------------------------------------------
1 | package com.titizz.exercise.distributedlock;
2 |
3 | /**
4 | * Created by code4wt on 17/8/26.
5 | */
6 | public enum LockStatus {
7 | TRY_LOCK,
8 | LOCKED,
9 | UNLOCK
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/com/titizz/exercise/distributedlock/ReadWriteLock.java:
--------------------------------------------------------------------------------
1 | package com.titizz.exercise.distributedlock;
2 |
3 | /**
4 | * Created by code4wt on 17/8/26.
5 | */
6 | public interface ReadWriteLock {
7 |
8 | DistributedLock readLock();
9 | DistributedLock writeLock();
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/com/titizz/exercise/distributedlock/DistributedLock.java:
--------------------------------------------------------------------------------
1 | package com.titizz.exercise.distributedlock;
2 |
3 | /**
4 | * Created by code4wt on 17/8/26.
5 | */
6 | public interface DistributedLock {
7 |
8 | void lock() throws Exception;
9 | Boolean tryLock() throws Exception;
10 | Boolean tryLock(long millisecond) throws Exception;
11 | void unlock() throws Exception;
12 | }
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled class file
2 | *.class
3 |
4 | # Log file
5 | *.log
6 |
7 | # BlueJ files
8 | *.ctxt
9 |
10 | # Mobile Tools for Java (J2ME)
11 | .mtj.tmp/
12 |
13 | # Package Files #
14 | *.jar
15 | *.war
16 | *.ear
17 | *.zip
18 | *.tar.gz
19 | *.rar
20 |
21 | *.rdb
22 |
23 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
24 | hs_err_pid*
25 |
26 | short-url.iml
27 | *.iml
28 | .idea
29 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.titizz.exercise
8 | distributed_lock
9 | 1.0-SNAPSHOT
10 |
11 |
12 |
13 | org.apache.zookeeper
14 | zookeeper
15 | 3.4.10
16 |
17 |
18 | junit
19 | junit
20 | 4.12
21 | test
22 |
23 |
24 |
25 |
26 |
27 |
28 | org.apache.maven.plugins
29 | maven-compiler-plugin
30 | 3.6.1
31 |
32 | 1.8
33 | 1.8
34 | ut
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/src/test/java/com/titizz/exercise/distributedlock/ZKWriteLockTest2.java:
--------------------------------------------------------------------------------
1 | package com.titizz.exercise.distributedlock;
2 |
3 | import org.junit.Test;
4 |
5 | import java.util.Random;
6 | import java.util.concurrent.ExecutorService;
7 | import java.util.concurrent.Executors;
8 | import java.util.concurrent.TimeUnit;
9 |
10 | /**
11 | * Created by code4wt on 17/8/27.
12 | */
13 | public class ZKWriteLockTest2 {
14 | @Test
15 | public void lock() throws Exception {
16 | Runnable runnable = () -> {
17 | try {
18 | ZKReadWriteLock2 crwl = new ZKReadWriteLock2();
19 | crwl.writeLock().lock();
20 | Thread.sleep(1000 + new Random(System.nanoTime()).nextInt(2000));
21 | crwl.writeLock().unlock();
22 | } catch (Exception e) {
23 | e.printStackTrace();
24 | }
25 | };
26 |
27 | int poolSize = 3;
28 | ExecutorService executorService = Executors.newFixedThreadPool(poolSize);
29 | for (int i = 0; i < poolSize; i++) {
30 | executorService.submit(runnable);
31 | Thread.sleep(10);
32 | }
33 |
34 | executorService.awaitTermination(10, TimeUnit.SECONDS);
35 | }
36 |
37 | @Test
38 | public void tryLock() throws Exception {
39 | ZKReadWriteLock2 crwl = new ZKReadWriteLock2();
40 | Boolean locked = crwl.writeLock().tryLock();
41 | System.out.println("locked: " + locked);
42 | crwl.writeLock().unlock();
43 | }
44 |
45 | @Test
46 | public void tryLock1() throws Exception {
47 | }
48 | }
--------------------------------------------------------------------------------
/src/test/java/com/titizz/exercise/distributedlock/ExclusiveLockTest.java:
--------------------------------------------------------------------------------
1 | package com.titizz.exercise.distributedlock;
2 |
3 | import org.junit.Test;
4 |
5 | import java.util.concurrent.ExecutorService;
6 | import java.util.concurrent.Executors;
7 | import java.util.concurrent.TimeUnit;
8 |
9 | /**
10 | * Created by code4wt on 17/8/24.
11 | */
12 | public class ExclusiveLockTest {
13 |
14 | @Test
15 | public void lock() throws Exception {
16 | Runnable runnable = () -> {
17 | try {
18 | DistributedLock lock = new ExclusiveLock();
19 | lock.lock();
20 | Thread.sleep(2000);
21 | lock.unlock();
22 | } catch (Exception e) {
23 | e.printStackTrace();
24 | }
25 | };
26 |
27 | int poolSize = 4;
28 | ExecutorService executorService = Executors.newFixedThreadPool(poolSize);
29 | for (int i = 0; i < poolSize; i++) {
30 | executorService.submit(runnable);
31 | }
32 |
33 | executorService.awaitTermination(10, TimeUnit.SECONDS);
34 | }
35 |
36 | @Test
37 | public void tryLock() throws Exception {
38 | ExclusiveLock lock = new ExclusiveLock();
39 | Boolean locked = lock.tryLock();
40 | System.out.println("locked: " + locked);
41 | }
42 |
43 | @Test
44 | public void tryLock1() throws Exception {
45 | ExclusiveLock lock = new ExclusiveLock();
46 | Boolean locked = lock.tryLock(50000);
47 | System.out.println("locked: " + locked);
48 | }
49 |
50 | @Test
51 | public void unlock() throws Exception {
52 | }
53 | }
--------------------------------------------------------------------------------
/src/test/java/com/titizz/exercise/distributedlock/ZKReadLockTest2.java:
--------------------------------------------------------------------------------
1 | package com.titizz.exercise.distributedlock;
2 |
3 | import org.junit.Test;
4 |
5 | import java.util.Random;
6 | import java.util.concurrent.ExecutorService;
7 | import java.util.concurrent.Executors;
8 | import java.util.concurrent.TimeUnit;
9 |
10 | /**
11 | * Created by code4wt on 17/8/27.
12 | */
13 | public class ZKReadLockTest2 {
14 |
15 | @Test
16 | public void lock() throws Exception {
17 | Runnable runnable = () -> {
18 | try {
19 | ZKReadWriteLock2 crwl = new ZKReadWriteLock2();
20 | crwl.readLock().lock();
21 | Thread.sleep(1000 + new Random(System.nanoTime()).nextInt(2000));
22 | crwl.readLock().unlock();
23 | } catch (Exception e) {
24 | e.printStackTrace();
25 | }
26 | };
27 |
28 | int poolSize = 4;
29 | ExecutorService executorService = Executors.newFixedThreadPool(poolSize);
30 | for (int i = 0; i < poolSize; i++) {
31 | executorService.submit(runnable);
32 | }
33 |
34 | executorService.awaitTermination(10, TimeUnit.SECONDS);
35 | }
36 |
37 | @Test
38 | public void tryLock() throws Exception {
39 | ZKReadWriteLock2 crwl = new ZKReadWriteLock2();
40 | Boolean locked = crwl.readLock().tryLock();
41 | System.out.println("locked: " + locked);
42 | crwl.readLock().unlock();
43 | }
44 |
45 | @Test
46 | public void tryLock1() throws Exception {
47 | ZKReadWriteLock2 crwl = new ZKReadWriteLock2();
48 | Boolean locked = crwl.readLock().tryLock(20000);
49 | System.out.println("locked: " + locked);
50 | crwl.readLock().unlock();
51 | }
52 |
53 | }
--------------------------------------------------------------------------------
/src/test/java/com/titizz/exercise/distributedlock/ZKReadLockTest1.java:
--------------------------------------------------------------------------------
1 | package com.titizz.exercise.distributedlock;
2 |
3 | import org.junit.Test;
4 |
5 | import java.util.Random;
6 | import java.util.concurrent.ExecutorService;
7 | import java.util.concurrent.Executors;
8 | import java.util.concurrent.TimeUnit;
9 |
10 | /**
11 | * Created by code4wt on 17/8/27.
12 | */
13 | public class ZKReadLockTest1 {
14 |
15 | @Test
16 | public void lock() throws Exception {
17 | Runnable runnable = () -> {
18 | try {
19 | ZKReadWriteLock1 srwl = new ZKReadWriteLock1();
20 | srwl.readLock().lock();
21 | Thread.sleep(1000 + new Random(System.nanoTime()).nextInt(2000));
22 | srwl.readLock().unlock();
23 | } catch (Exception e) {
24 | e.printStackTrace();
25 | }
26 | };
27 |
28 | int poolSize = 4;
29 | ExecutorService executorService = Executors.newFixedThreadPool(poolSize);
30 | for (int i = 0; i < poolSize; i++) {
31 | Thread.sleep(10);
32 | executorService.submit(runnable);
33 | }
34 |
35 | executorService.awaitTermination(10, TimeUnit.SECONDS);
36 | }
37 |
38 | @Test
39 | public void tryLock() throws Exception {
40 | ZKReadWriteLock1 srwl = new ZKReadWriteLock1();
41 | Boolean locked = srwl.readLock().tryLock();
42 | System.out.println("locked: " + locked);
43 | srwl.readLock().unlock();
44 | }
45 |
46 | @Test
47 | public void tryLock1() throws Exception {
48 | ZKReadWriteLock1 srwl = new ZKReadWriteLock1();
49 | Boolean locked = srwl.readLock().tryLock(20000);
50 | System.out.println("locked: " + locked);
51 | srwl.readLock().unlock();
52 | }
53 |
54 | @Test
55 | public void unlock() throws Exception {
56 | }
57 | }
--------------------------------------------------------------------------------
/src/test/java/com/titizz/exercise/distributedlock/ZKWriteLockTest1.java:
--------------------------------------------------------------------------------
1 | package com.titizz.exercise.distributedlock;
2 |
3 | import org.junit.Test;
4 |
5 | import java.util.Random;
6 | import java.util.concurrent.ExecutorService;
7 | import java.util.concurrent.Executors;
8 | import java.util.concurrent.TimeUnit;
9 |
10 | /**
11 | * Created by code4wt on 17/8/27.
12 | */
13 | public class ZKWriteLockTest1 {
14 | @Test
15 | public void lock() throws Exception {
16 | Runnable runnable = () -> {
17 | try {
18 | ZKReadWriteLock1 srwl = new ZKReadWriteLock1();
19 | srwl.writeLock().lock();
20 | Thread.sleep(1000 + new Random(System.nanoTime()).nextInt(2000));
21 | srwl.writeLock().unlock();
22 | } catch (Exception e) {
23 | e.printStackTrace();
24 | }
25 | };
26 |
27 | int poolSize = 4;
28 | ExecutorService executorService = Executors.newFixedThreadPool(poolSize);
29 | for (int i = 0; i < poolSize; i++) {
30 | Thread.sleep(10);
31 | executorService.submit(runnable);
32 | }
33 |
34 | executorService.awaitTermination(10, TimeUnit.SECONDS);
35 | }
36 |
37 | @Test
38 | public void tryLock() throws Exception {
39 | ZKReadWriteLock1 srwl = new ZKReadWriteLock1();
40 | Boolean locked = srwl.writeLock().tryLock();
41 | System.out.println("locked: " + locked);
42 | srwl.writeLock().unlock();
43 | }
44 |
45 | @Test
46 | public void tryLock1() throws Exception {
47 | ZKReadWriteLock1 srwl = new ZKReadWriteLock1();
48 | Boolean locked = srwl.writeLock().tryLock(20000);
49 | System.out.println("locked: " + locked);
50 | srwl.writeLock().unlock();
51 | }
52 |
53 | @Test
54 | public void unlock() throws Exception {
55 | }
56 | }
--------------------------------------------------------------------------------
/src/main/java/com/titizz/exercise/distributedlock/ExclusiveLock.java:
--------------------------------------------------------------------------------
1 | package com.titizz.exercise.distributedlock;
2 |
3 | import org.apache.zookeeper.*;
4 | import org.apache.zookeeper.Watcher.Event.EventType;
5 | import org.apache.zookeeper.Watcher.Event.KeeperState;
6 | import org.apache.zookeeper.data.Stat;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 |
10 | import java.io.IOException;
11 | import java.util.Random;
12 | import java.util.concurrent.BrokenBarrierException;
13 | import java.util.concurrent.CountDownLatch;
14 | import java.util.concurrent.CyclicBarrier;
15 |
16 | /**
17 | * Created by code4wt on 17/8/24.
18 | */
19 | public class ExclusiveLock implements DistributedLock {
20 |
21 | private static final Logger logger = LoggerFactory.getLogger(ExclusiveLock.class);
22 |
23 | private static final String LOCK_NODE_FULL_PATH = "/exclusive_lock/lock";
24 |
25 | /** 自旋测试超时阈值,考虑到网络的延时性,这里设为1000毫秒 */
26 | private static final long spinForTimeoutThreshold = 1000L;
27 |
28 | private static final long SLEEP_TIME = 100L;
29 |
30 | private ZooKeeper zooKeeper;
31 |
32 | private CountDownLatch connectedSemaphore = new CountDownLatch(1);
33 |
34 | private CyclicBarrier lockBarrier = new CyclicBarrier(2);
35 |
36 | private LockStatus lockStatus;
37 |
38 | private String id = String.valueOf(new Random(System.nanoTime()).nextInt(10000000));
39 |
40 | public ExclusiveLock() throws InterruptedException, IOException {
41 | zooKeeper = new ZooKeeper("127.0.0.1:2181", 1000, new LockNodeWatcher());
42 | lockStatus = LockStatus.UNLOCK;
43 | connectedSemaphore.await();
44 | }
45 |
46 | public void lock() throws Exception {
47 | if (lockStatus != LockStatus.UNLOCK) {
48 | return;
49 | }
50 |
51 | // 1. 创建锁节点
52 | if (createLockNode()) {
53 | System.out.println("[" + id + "]" + " 获取锁");
54 | lockStatus = LockStatus.LOCKED;
55 | return;
56 | }
57 |
58 | lockStatus = LockStatus.TRY_LOCK;
59 | lockBarrier.await();
60 | }
61 |
62 | public Boolean tryLock() {
63 | if (lockStatus == LockStatus.LOCKED) {
64 | return true;
65 | }
66 |
67 | Boolean created = createLockNode();
68 | lockStatus = created ? LockStatus.LOCKED : LockStatus.UNLOCK;
69 | return created;
70 | }
71 |
72 | public Boolean tryLock(long millisecond) throws Exception {
73 | long millisTimeout = millisecond;
74 | if (millisTimeout <= 0L) {
75 | return false;
76 | }
77 |
78 | final long deadline = System.currentTimeMillis() + millisTimeout;
79 | for (;;) {
80 | if (tryLock()) {
81 | return true;
82 | }
83 |
84 | if (millisTimeout > spinForTimeoutThreshold) {
85 | Thread.sleep(SLEEP_TIME);
86 | }
87 |
88 | millisTimeout = deadline - System.currentTimeMillis();
89 | if (millisTimeout <= 0L) {
90 | return false;
91 | }
92 | }
93 | }
94 |
95 | public void unlock() throws Exception {
96 | if (lockStatus == LockStatus.UNLOCK) {
97 | return;
98 | }
99 |
100 | deleteLockNode();
101 | lockStatus = LockStatus.UNLOCK;
102 | lockBarrier.reset();
103 | System.out.println("[" + id + "]" + " 释放锁");
104 | }
105 |
106 | private Boolean createLockNode() {
107 | try {
108 | zooKeeper.create(LOCK_NODE_FULL_PATH, "".getBytes(),
109 | ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
110 | } catch (KeeperException | InterruptedException e) {
111 | return false;
112 | }
113 |
114 | return true;
115 | }
116 |
117 | private void deleteLockNode() throws KeeperException, InterruptedException {
118 | Stat stat = zooKeeper.exists(LOCK_NODE_FULL_PATH, false);
119 | zooKeeper.delete(LOCK_NODE_FULL_PATH, stat.getVersion());
120 | }
121 |
122 | enum LockStatus {
123 | TRY_LOCK,
124 | LOCKED,
125 | UNLOCK
126 | }
127 |
128 | class LockNodeWatcher implements Watcher {
129 |
130 | public void process(WatchedEvent event) {
131 | if (KeeperState.SyncConnected != event.getState()) {
132 | return;
133 | }
134 |
135 | // 2. 设置监视器
136 | try {
137 | zooKeeper.exists(LOCK_NODE_FULL_PATH, this);
138 | } catch (KeeperException | InterruptedException e) {
139 | e.printStackTrace();
140 | }
141 |
142 | if (EventType.None == event.getType() && event.getPath() == null) {
143 | connectedSemaphore.countDown();
144 | } else if (EventType.NodeDeleted == event.getType()
145 | && event.getPath().equals(LOCK_NODE_FULL_PATH)) {
146 |
147 | // 3. 再次尝试创建锁及诶单
148 | if (lockStatus == LockStatus.TRY_LOCK && createLockNode()) {
149 | lockStatus = LockStatus.LOCKED;
150 | try {
151 | lockBarrier.await();
152 | System.out.println("[" + id + "]" + " 获取锁");
153 | return;
154 | } catch (InterruptedException | BrokenBarrierException e) {
155 | e.printStackTrace();
156 | }
157 | }
158 | }
159 | }
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/src/main/java/com/titizz/exercise/distributedlock/ZKReadWriteLock2.java:
--------------------------------------------------------------------------------
1 | package com.titizz.exercise.distributedlock;
2 |
3 | import org.apache.zookeeper.*;
4 | import org.apache.zookeeper.Watcher.Event.EventType;
5 | import org.apache.zookeeper.Watcher.Event.KeeperState;
6 | import org.apache.zookeeper.data.Stat;
7 |
8 | import java.util.*;
9 | import java.util.concurrent.BrokenBarrierException;
10 | import java.util.concurrent.CountDownLatch;
11 | import java.util.concurrent.CyclicBarrier;
12 |
13 | /**
14 | * Created by code4wt on 17/8/27.
15 | */
16 | public class ZKReadWriteLock2 implements ReadWriteLock {
17 |
18 | private static final String LOCK_NODE_PARENT_PATH = "/share_lock";
19 |
20 | /** 自旋测试超时阈值,考虑到网络的延时性,这里设为1000毫秒 */
21 | private static final long spinForTimeoutThreshold = 1000L;
22 |
23 | private static final long SLEEP_TIME = 100L;
24 |
25 | private ZooKeeper zooKeeper;
26 |
27 | private CountDownLatch connectedSemaphore = new CountDownLatch(1);
28 |
29 | private ReadLock readLock = new ReadLock();
30 |
31 | private WriteLock writeLock = new WriteLock();
32 |
33 | private Comparator nameComparator;
34 |
35 | public ZKReadWriteLock2() throws Exception {
36 | Watcher watcher = event -> {
37 | if (KeeperState.SyncConnected == event.getState()) {
38 | connectedSemaphore.countDown();
39 | }
40 | };
41 | zooKeeper = new ZooKeeper("127.0.0.1:2181", 1000, watcher);
42 | connectedSemaphore.await();
43 |
44 | nameComparator = (x, y) -> {
45 | Integer xs = getSequence(x);
46 | Integer ys = getSequence(y);
47 | return xs > ys ? 1 : (xs < ys ? -1 : 0);
48 | };
49 | }
50 |
51 | @Override
52 | public DistributedLock readLock() {
53 | return readLock;
54 | }
55 |
56 | @Override
57 | public DistributedLock writeLock() {
58 | return writeLock;
59 | }
60 |
61 | class ReadLock implements DistributedLock, Watcher {
62 |
63 | private LockStatus lockStatus = LockStatus.UNLOCK;
64 |
65 | private CyclicBarrier lockBarrier = new CyclicBarrier(2);
66 |
67 | private String prefix = new Random(System.nanoTime()).nextInt(10000000) + "-read-";
68 |
69 | private String name;
70 |
71 | @Override
72 | public void lock() throws Exception {
73 | if (lockStatus == LockStatus.LOCKED) {
74 | return;
75 | }
76 |
77 | // 1. 创建锁节点
78 | if (name == null) {
79 | name = createLockNode(prefix);
80 | name = name.substring(name.lastIndexOf("/") + 1);
81 | System.out.println("创建锁节点 " + name);
82 | }
83 |
84 | // 2. 获取锁节点列表
85 | List nodes = zooKeeper.getChildren(LOCK_NODE_PARENT_PATH, this);
86 | nodes.sort(nameComparator);
87 |
88 | // 3. 检查能否获取锁,若能,直接返回
89 | if (canAcquireLock(name, nodes)) {
90 | System.out.println(name + " 获取锁");
91 | lockStatus = LockStatus.LOCKED;
92 | return;
93 | }
94 |
95 | // 4. 不能获取锁,找到比自己小的最后一个的写锁节点,并监视
96 | int index = Collections.binarySearch(nodes, name, nameComparator);
97 | for (int i = index - 1; i >= 0; i--) {
98 | if (nodes.get(i).contains("write")) {
99 | zooKeeper.exists(LOCK_NODE_PARENT_PATH + "/" + nodes.get(i), this);
100 | break;
101 | }
102 | }
103 |
104 | // 5. 等待监视的节点被删除
105 | lockStatus = LockStatus.TRY_LOCK;
106 | lockBarrier.await();
107 | }
108 |
109 | @Override
110 | public Boolean tryLock() throws Exception {
111 | if (lockStatus == LockStatus.LOCKED) {
112 | return true;
113 | }
114 |
115 | // 1. 创建锁节点
116 | if (name == null) {
117 | name = createLockNode(prefix);
118 | name = name.substring(name.lastIndexOf("/") + 1);
119 | System.out.println("创建锁节点 " + name);
120 | }
121 |
122 | // 2. 获取锁节点列表
123 | List nodes = zooKeeper.getChildren(LOCK_NODE_PARENT_PATH, null);
124 | nodes.sort(nameComparator);
125 |
126 | // 3. 检查能否获取锁
127 | if (canAcquireLock(name, nodes)) {
128 | System.out.println(name + " 获取锁");
129 | lockStatus = LockStatus.LOCKED;
130 | return true;
131 | }
132 |
133 | return false;
134 | }
135 |
136 | @Override
137 | public Boolean tryLock(long millisecond) throws Exception {
138 | long millisTimeout = millisecond;
139 | if (millisTimeout <= 0L) {
140 | return false;
141 | }
142 |
143 | final long deadline = System.currentTimeMillis() + millisTimeout;
144 | for (;;) {
145 | if (tryLock()) {
146 | return true;
147 | }
148 |
149 | if (millisTimeout > spinForTimeoutThreshold) {
150 | Thread.sleep(SLEEP_TIME);
151 | }
152 |
153 | millisTimeout = deadline - System.currentTimeMillis();
154 | if (millisTimeout <= 0L) {
155 | return false;
156 | }
157 | }
158 | }
159 |
160 | @Override
161 | public void unlock() throws Exception {
162 | if (lockStatus == LockStatus.UNLOCK) {
163 | return;
164 | }
165 |
166 | deleteLockNode(name);
167 | lockStatus = LockStatus.UNLOCK;
168 | lockBarrier.reset();
169 | System.out.println(name + " 释放锁");
170 | name = null;
171 | }
172 |
173 | @Override
174 | public void process(WatchedEvent event) {
175 | if (KeeperState.SyncConnected != event.getState()) {
176 | return;
177 | }
178 |
179 | if (EventType.None == event.getType() && event.getPath() == null) {
180 | connectedSemaphore.countDown();
181 | } else if (EventType.NodeDeleted == event.getType()) {
182 | if (lockStatus != LockStatus.TRY_LOCK) {
183 | return;
184 | }
185 |
186 | System.out.println(name + " 获取锁");
187 | lockStatus = LockStatus.LOCKED;
188 | try {
189 | lockBarrier.await();
190 | } catch (InterruptedException | BrokenBarrierException e) {
191 | e.printStackTrace();
192 | }
193 | }
194 | }
195 | }
196 |
197 | class WriteLock implements DistributedLock, Watcher {
198 |
199 | private LockStatus lockStatus = LockStatus.UNLOCK;
200 |
201 | private CyclicBarrier lockBarrier = new CyclicBarrier(2);
202 |
203 | private String prefix = new Random(System.nanoTime()).nextInt(1000000) + "-write-";
204 |
205 | private String name;
206 |
207 | @Override
208 | public void lock() throws Exception {
209 | if (lockStatus == LockStatus.LOCKED) {
210 | return;
211 | }
212 |
213 | // 1. 创建锁节点
214 | if (name == null) {
215 | name = createLockNode(prefix);
216 | name = name.substring(name.lastIndexOf("/") + 1);
217 | System.out.println("创建锁节点 " + name);
218 | }
219 |
220 | // 2. 获取锁节点列表
221 | List nodes = zooKeeper.getChildren(LOCK_NODE_PARENT_PATH, null);
222 | nodes.sort(nameComparator);
223 |
224 | // 3. 检查自己是否是排在第一位,若是,加锁成功
225 | if (isFirstNode(name, nodes)) {
226 | System.out.println(name + " 获取锁");
227 | lockStatus = LockStatus.LOCKED;
228 | return;
229 | }
230 |
231 | // 4. 若不是,定位到上一个锁节点,并监视
232 | int index = Collections.binarySearch(nodes, name, nameComparator);
233 | zooKeeper.exists(LOCK_NODE_PARENT_PATH + "/" + nodes.get(index - 1), this);
234 |
235 | // 5. 等待监视的节点被删除
236 | lockStatus = LockStatus.TRY_LOCK;
237 | lockBarrier.await();
238 | }
239 |
240 | @Override
241 | public Boolean tryLock() throws Exception {
242 | if (lockStatus == LockStatus.LOCKED) {
243 | return true;
244 | }
245 |
246 | // 1. 创建锁节点
247 | if (name == null) {
248 | name = createLockNode(prefix);
249 | name = name.substring(name.lastIndexOf("/") + 1);
250 | System.out.println("创建锁节点 " + name);
251 | }
252 |
253 | // 2. 获取锁节点列表
254 | List nodes = zooKeeper.getChildren(LOCK_NODE_PARENT_PATH, null);
255 | nodes.sort(nameComparator);
256 |
257 | // 3. 检查自己是否是排在第一位,若是,加锁成功
258 | if (isFirstNode(name, nodes)) {
259 | System.out.println(name + " 获取锁");
260 | lockStatus = LockStatus.LOCKED;
261 | return true;
262 | }
263 |
264 | return false;
265 | }
266 |
267 | @Override
268 | public Boolean tryLock(long millisecond) throws Exception {
269 | long millisTimeout = millisecond;
270 | if (millisTimeout <= 0L) {
271 | return false;
272 | }
273 |
274 | final long deadline = System.currentTimeMillis() + millisTimeout;
275 | for (;;) {
276 | if (tryLock()) {
277 | return true;
278 | }
279 |
280 | if (millisTimeout > spinForTimeoutThreshold) {
281 | Thread.sleep(SLEEP_TIME);
282 | }
283 |
284 | millisTimeout = deadline - System.currentTimeMillis();
285 | if (millisTimeout <= 0L) {
286 | return false;
287 | }
288 | }
289 | }
290 |
291 | @Override
292 | public void unlock() throws Exception {
293 | if (lockStatus == LockStatus.UNLOCK) {
294 | return;
295 | }
296 |
297 | System.out.println(name + " 释放锁");
298 | deleteLockNode(name);
299 | lockStatus = LockStatus.UNLOCK;
300 | lockBarrier.reset();
301 | name = null;
302 | }
303 |
304 | @Override
305 | public void process(WatchedEvent event) {
306 | if (KeeperState.SyncConnected != event.getState()) {
307 | return;
308 | }
309 |
310 | if (EventType.None == event.getType() && event.getPath() == null) {
311 | connectedSemaphore.countDown();
312 | } else if (EventType.NodeDeleted == event.getType()) {
313 | if (lockStatus != LockStatus.TRY_LOCK) {
314 | return;
315 | }
316 |
317 | lockStatus = LockStatus.LOCKED;
318 | try {
319 | lockBarrier.await();
320 | System.out.println(name + " 获取锁");
321 | } catch (InterruptedException | BrokenBarrierException e) {
322 | e.printStackTrace();
323 | }
324 | }
325 | }
326 | }
327 |
328 | private Integer getSequence(String name) {
329 | return Integer.valueOf(name.substring(name.lastIndexOf("-") + 1));
330 | }
331 |
332 | private String createLockNode(String name) {
333 | String path = null;
334 | try {
335 | path = zooKeeper.create(LOCK_NODE_PARENT_PATH + "/" + name, "".getBytes(),
336 | ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
337 | } catch (KeeperException | InterruptedException e) {
338 | e.printStackTrace();
339 | return null;
340 | }
341 |
342 | return path;
343 | }
344 |
345 | private void deleteLockNode(String name) throws KeeperException, InterruptedException {
346 | Stat stat = zooKeeper.exists(LOCK_NODE_PARENT_PATH + "/" + name, false);
347 | zooKeeper.delete(LOCK_NODE_PARENT_PATH + "/" + name, stat.getVersion());
348 | }
349 |
350 | private Boolean canAcquireLock(String name, List nodes) {
351 | if (isFirstNode(name, nodes)) {
352 | return true;
353 | }
354 |
355 | Map map = new HashMap<>();
356 | boolean hasWriteoperation = false;
357 | for (String n : nodes) {
358 | if (n.contains("read") && !hasWriteoperation) {
359 | map.put(n, true);
360 | } else {
361 | hasWriteoperation = true;
362 | map.put((n), false);
363 | }
364 | }
365 |
366 | return map.get(name);
367 | }
368 |
369 | private Boolean isFirstNode(String name, List nodes) {
370 | return nodes.get(0).equals(name);
371 | }
372 | }
373 |
--------------------------------------------------------------------------------
/src/main/java/com/titizz/exercise/distributedlock/ZKReadWriteLock1.java:
--------------------------------------------------------------------------------
1 | package com.titizz.exercise.distributedlock;
2 |
3 | import org.apache.zookeeper.*;
4 | import org.apache.zookeeper.Watcher.Event.EventType;
5 | import org.apache.zookeeper.Watcher.Event.KeeperState;
6 | import org.apache.zookeeper.data.Stat;
7 |
8 | import java.util.*;
9 | import java.util.concurrent.BrokenBarrierException;
10 | import java.util.concurrent.CountDownLatch;
11 | import java.util.concurrent.CyclicBarrier;
12 |
13 | /**
14 | * Created by code4wt on 17/8/26.
15 | */
16 | public class ZKReadWriteLock1 implements ReadWriteLock {
17 |
18 | private static final String LOCK_NODE_PARENT_PATH = "/share_lock";
19 |
20 | /** 自旋测试超时阈值,考虑到网络的延时性,这里设为1000毫秒 */
21 | private static final long spinForTimeoutThreshold = 1000L;
22 |
23 | private static final long SLEEP_TIME = 100L;
24 |
25 | private ZooKeeper zooKeeper;
26 |
27 | private CountDownLatch connectedSemaphore = new CountDownLatch(1);
28 |
29 | private ReadLock readLock = new ReadLock();
30 |
31 | private WriteLock writeLock = new WriteLock();
32 |
33 | private Comparator nameComparator;
34 |
35 | public ZKReadWriteLock1() throws Exception {
36 | Watcher watcher = event -> {
37 | if (KeeperState.SyncConnected == event.getState()) {
38 | connectedSemaphore.countDown();
39 | }
40 | };
41 | zooKeeper = new ZooKeeper("127.0.0.1:2181", 1000, watcher);
42 | connectedSemaphore.await();
43 |
44 | nameComparator = (x, y) -> {
45 | Integer xs = getSequence(x);
46 | Integer ys = getSequence(y);
47 | return xs > ys ? 1 : (xs < ys ? -1 : 0);
48 | };
49 | }
50 |
51 | @Override
52 | public DistributedLock readLock() {
53 | return readLock;
54 | }
55 |
56 | @Override
57 | public DistributedLock writeLock() {
58 | return writeLock;
59 | }
60 |
61 | private class ReadLock implements DistributedLock, Watcher {
62 |
63 | private LockStatus lockStatus = LockStatus.UNLOCK;
64 |
65 | private CyclicBarrier lockBarrier = new CyclicBarrier(2);
66 |
67 | private String prefix = new Random(System.nanoTime()).nextInt(10000000) + "-read-";
68 |
69 | private String name;
70 |
71 | @Override
72 | public void lock() throws Exception {
73 | if (lockStatus != LockStatus.UNLOCK) {
74 | return;
75 | }
76 |
77 | // 1. 创建锁节点
78 | if (name == null) {
79 | name = createLockNode(prefix);
80 | name = name.substring(name.lastIndexOf("/") + 1);
81 | System.out.println("创建锁节点 " + name);
82 | }
83 |
84 | // 2. 获取锁节点列表,并设置监视器
85 | List nodes = zooKeeper.getChildren(LOCK_NODE_PARENT_PATH, this);
86 | nodes.sort(nameComparator);
87 |
88 | // 3. 检查能否获取锁,若能,直接返回
89 | if (canAcquireLock(name, nodes)) {
90 | System.out.println(name + " 获取锁");
91 | lockStatus = LockStatus.LOCKED;
92 | return;
93 | }
94 |
95 | lockStatus = LockStatus.TRY_LOCK;
96 | lockBarrier.await();
97 | }
98 |
99 | @Override
100 | public Boolean tryLock() throws Exception {
101 | if (lockStatus == LockStatus.LOCKED) {
102 | return true;
103 | }
104 |
105 | if (name == null) {
106 | name = createLockNode(prefix);
107 | name = name.substring(name.lastIndexOf("/") + 1);
108 | System.out.println("创建锁节点 " + name);
109 | }
110 |
111 | List nodes = zooKeeper.getChildren(LOCK_NODE_PARENT_PATH, this);
112 | nodes.sort(nameComparator);
113 |
114 | if (canAcquireLock(name, nodes)) {
115 | lockStatus = LockStatus.LOCKED;
116 | return true;
117 | }
118 |
119 | return false;
120 | }
121 |
122 | @Override
123 | public Boolean tryLock(long millisecond) throws Exception {
124 | long millisTimeout = millisecond;
125 | if (millisTimeout <= 0L) {
126 | return false;
127 | }
128 |
129 | final long deadline = System.currentTimeMillis() + millisTimeout;
130 | for (;;) {
131 | if (tryLock()) {
132 | return true;
133 | }
134 |
135 | if (millisTimeout > spinForTimeoutThreshold) {
136 | Thread.sleep(SLEEP_TIME);
137 | }
138 |
139 | millisTimeout = deadline - System.currentTimeMillis();
140 | if (millisTimeout <= 0L) {
141 | return false;
142 | }
143 | }
144 | }
145 |
146 | @Override
147 | public void unlock() throws Exception {
148 | if (lockStatus == LockStatus.UNLOCK) {
149 | return;
150 | }
151 |
152 | deleteLockNode(name);
153 | lockStatus = LockStatus.UNLOCK;
154 | lockBarrier.reset();
155 | System.out.println(name + " 释放锁");
156 | name = null;
157 | }
158 |
159 | @Override
160 | public void process(WatchedEvent event) {
161 | if (KeeperState.SyncConnected != event.getState()) {
162 | return;
163 | }
164 |
165 | if (EventType.None == event.getType() && event.getPath() == null) {
166 | connectedSemaphore.countDown();
167 | } else if (EventType.NodeChildrenChanged == event.getType()
168 | && event.getPath().equals(LOCK_NODE_PARENT_PATH)) {
169 |
170 | if (lockStatus != LockStatus.TRY_LOCK) {
171 | return;
172 | }
173 |
174 | List nodes = null;
175 | try {
176 | // 获取锁列表
177 | nodes = zooKeeper.getChildren(LOCK_NODE_PARENT_PATH, this);
178 | nodes.sort(nameComparator);
179 | } catch (KeeperException | InterruptedException e) {
180 | e.printStackTrace();
181 | return;
182 | }
183 |
184 | // 判断前面是否有写操作 ? 获取锁 :等待
185 | if (canAcquireLock(name, nodes)) {
186 | lockStatus = LockStatus.LOCKED;
187 | try {
188 | lockBarrier.await();
189 | System.out.println(name + " 获取锁");
190 | } catch (InterruptedException | BrokenBarrierException e) {
191 | e.printStackTrace();
192 | }
193 | }
194 | }
195 | }
196 | }
197 |
198 | private class WriteLock implements DistributedLock, Watcher {
199 |
200 | private LockStatus lockStatus = LockStatus.UNLOCK;
201 |
202 | private CyclicBarrier lockBarrier = new CyclicBarrier(2);
203 |
204 | private String prefix = new Random(System.nanoTime()).nextInt(1000000) + "-write-";
205 |
206 | private String name;
207 |
208 | @Override
209 | public void lock() throws Exception {
210 | if (lockStatus != LockStatus.UNLOCK) {
211 | return;
212 | }
213 |
214 | if (name == null) {
215 | name = createLockNode(prefix);
216 | name = name.substring(name.lastIndexOf("/") + 1);
217 | System.out.println("创建锁节点 " + name);
218 | }
219 |
220 | List nodes = zooKeeper.getChildren(LOCK_NODE_PARENT_PATH, this);
221 | nodes.sort(nameComparator);
222 |
223 | if (isFirstNode(name, nodes)) {
224 | System.out.println(name + " 获取锁");
225 | lockStatus = LockStatus.LOCKED;
226 | return;
227 | }
228 |
229 | lockStatus = LockStatus.TRY_LOCK;
230 | lockBarrier.await();
231 | }
232 |
233 | @Override
234 | public Boolean tryLock() throws Exception {
235 | if (lockStatus == LockStatus.LOCKED) {
236 | return true;
237 | }
238 |
239 | if (name == null) {
240 | name = createLockNode(prefix);
241 | name = name.substring(name.lastIndexOf("/") + 1);
242 | System.out.println("创建锁节点 " + name);
243 | }
244 |
245 | List nodes = zooKeeper.getChildren(LOCK_NODE_PARENT_PATH, this);
246 | nodes.sort(nameComparator);
247 |
248 | if (isFirstNode(name, nodes)) {
249 | lockStatus = LockStatus.LOCKED;
250 | return true;
251 | }
252 |
253 | return false;
254 | }
255 |
256 | @Override
257 | public Boolean tryLock(long millisecond) throws Exception {
258 | long millisTimeout = millisecond;
259 | if (millisTimeout <= 0L) {
260 | return false;
261 | }
262 |
263 | final long deadline = System.currentTimeMillis() + millisTimeout;
264 | for (;;) {
265 | if (tryLock()) {
266 | return true;
267 | }
268 |
269 | if (millisTimeout > spinForTimeoutThreshold) {
270 | Thread.sleep(SLEEP_TIME);
271 | }
272 |
273 | millisTimeout = deadline - System.currentTimeMillis();
274 | if (millisTimeout <= 0L) {
275 | return false;
276 | }
277 | }
278 | }
279 |
280 | @Override
281 | public void unlock() throws Exception {
282 | if (lockStatus == LockStatus.UNLOCK) {
283 | return;
284 | }
285 |
286 | deleteLockNode(name);
287 | lockStatus = LockStatus.UNLOCK;
288 | lockBarrier.reset();
289 | System.out.println(name + " 释放锁");
290 | name = null;
291 | }
292 |
293 | @Override
294 | public void process(WatchedEvent event) {
295 | if (KeeperState.SyncConnected != event.getState()) {
296 | return;
297 | }
298 |
299 | if (EventType.None == event.getType() && event.getPath() == null) {
300 | connectedSemaphore.countDown();
301 | } else if (EventType.NodeChildrenChanged == event.getType()
302 | && event.getPath().equals(LOCK_NODE_PARENT_PATH)) {
303 |
304 | if (lockStatus != LockStatus.TRY_LOCK) {
305 | return;
306 | }
307 |
308 | List nodes = null;
309 | try {
310 | // 获取锁列表
311 | nodes = zooKeeper.getChildren(LOCK_NODE_PARENT_PATH, this);
312 | nodes.sort(nameComparator);
313 | } catch (KeeperException | InterruptedException e) {
314 | e.printStackTrace();
315 | return;
316 | }
317 |
318 | // 判断前面是否有写操作
319 | if (isFirstNode(name, nodes)) {
320 | lockStatus = LockStatus.LOCKED;
321 | try {
322 | lockBarrier.await();
323 | System.out.println(name + " 获取锁");
324 | } catch (InterruptedException | BrokenBarrierException e) {
325 | e.printStackTrace();
326 | }
327 | }
328 | }
329 | }
330 | }
331 |
332 | private String createLockNode(String name) {
333 | String path = null;
334 | try {
335 | path = zooKeeper.create(LOCK_NODE_PARENT_PATH + "/" + name, "".getBytes(),
336 | ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
337 | } catch (KeeperException | InterruptedException e) {
338 | System.out.println(" failed to create lock node");
339 | return null;
340 | }
341 |
342 | return path;
343 | }
344 |
345 | private void deleteLockNode(String name) throws KeeperException, InterruptedException {
346 | Stat stat = zooKeeper.exists(LOCK_NODE_PARENT_PATH + "/" + name, false);
347 | zooKeeper.delete(LOCK_NODE_PARENT_PATH + "/" + name, stat.getVersion());
348 | }
349 |
350 | private String getPrefix(String name) {
351 | return name.substring(0, name.lastIndexOf('-') + 1);
352 | }
353 |
354 | private Integer getSequence(String name) {
355 | return Integer.valueOf(name.substring(name.lastIndexOf("-") + 1));
356 | }
357 |
358 | private Boolean canAcquireLock(String name, List nodes) {
359 | if (isFirstNode(name, nodes)) {
360 | return true;
361 | }
362 |
363 | Map map = new HashMap<>();
364 | boolean hasWriteoperation = false;
365 | for (String n : nodes) {
366 | if (n.contains("read") && !hasWriteoperation) {
367 | map.put(n, true);
368 | } else {
369 | hasWriteoperation = true;
370 | map.put((n), false);
371 | }
372 | }
373 |
374 | return map.get(name);
375 | }
376 |
377 | private Boolean isFirstNode(String name, List nodes) {
378 | return nodes.get(0).equals(name);
379 | }
380 | }
--------------------------------------------------------------------------------