├── 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 | } --------------------------------------------------------------------------------