├── .gitignore ├── .settings ├── org.eclipse.m2e.core.prefs ├── org.eclipse.core.resources.prefs └── org.eclipse.jdt.core.prefs ├── .project ├── .classpath ├── src ├── main │ └── java │ │ └── com │ │ └── jason │ │ └── pandaLock │ │ └── core │ │ ├── server │ │ └── DistributedLock.java │ │ ├── exception │ │ └── PandaLockException.java │ │ ├── bean │ │ ├── LockInfo.java │ │ └── CompetitorInfo.java │ │ └── serverImpl │ │ └── ZkPandaLock.java └── test │ └── java │ └── com │ └── jason │ └── distributedlock │ └── LockTest.java └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | -------------------------------------------------------------------------------- /.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | activeProfiles= 2 | eclipse.preferences.version=1 3 | resolveWorkspaceProjects=true 4 | version=1 5 | -------------------------------------------------------------------------------- /.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding//src/main/java=UTF-8 3 | encoding//src/test/java=UTF-8 4 | encoding/=UTF-8 5 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 3 | org.eclipse.jdt.core.compiler.compliance=1.7 4 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 5 | org.eclipse.jdt.core.compiler.source=1.7 6 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | distributedLock 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.m2e.core.maven2Builder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | org.eclipse.m2e.core.maven2Nature 22 | 23 | 24 | -------------------------------------------------------------------------------- /.classpath: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /src/main/java/com/jason/pandaLock/core/server/DistributedLock.java: -------------------------------------------------------------------------------- 1 | package com.jason.pandaLock.core.server; 2 | 3 | import org.apache.zookeeper.KeeperException; 4 | 5 | import com.jason.pandaLock.core.exception.PandaLockException; 6 | 7 | /** 8 | * @author 作者 E-mail:ruanjianlxm@sina.com 9 | * @version 创建时间:2015年9月11日 下午6:27:55 10 | * 类说明 定义分布式锁需要的方法 11 | */ 12 | public abstract class DistributedLock { 13 | /** 14 | * 释放锁 15 | * @throws PandaLockException 16 | * @throws KeeperException 17 | * @throws InterruptedException 18 | */ 19 | public abstract void releaseLock(); 20 | /** 21 | * 尝试获得锁,能获得就立马获得锁,如果不能获得就立马返回 22 | * @throws PandaLockException 23 | * @throws ConnectException 24 | */ 25 | public abstract boolean tryLock(); 26 | /** 27 | * 尝试获得锁,如果有锁就返回,如果没有锁就等待,如果等待了一段时间后还没能获取到锁,那么就返回 28 | * @param timeout 单位:秒 29 | * @return 30 | */ 31 | public abstract boolean tryLock(int timeout); 32 | /** 33 | * 尝试获得锁,一直阻塞,直到获得锁为止 34 | * @param timeout 单位:秒 35 | * @throws PandaLockException 36 | * @throws ConnectException 37 | */ 38 | public abstract void lock() ; 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/jason/pandaLock/core/exception/PandaLockException.java: -------------------------------------------------------------------------------- 1 | package com.jason.pandaLock.core.exception; 2 | /** 3 | * @author 作者 E-mail:ruanjianlxm@sina.com 4 | * @version 创建时间:2015年9月11日 下午10:55:51 5 | * 类说明 6 | */ 7 | public class PandaLockException extends RuntimeException { 8 | 9 | /** 10 | * 11 | */ 12 | private static final long serialVersionUID = 1L; 13 | 14 | public PandaLockException() { 15 | super(); 16 | // TODO Auto-generated constructor stub 17 | } 18 | 19 | public PandaLockException(String message, Throwable cause, 20 | boolean enableSuppression, boolean writableStackTrace) { 21 | super(message, cause, enableSuppression, writableStackTrace); 22 | // TODO Auto-generated constructor stub 23 | } 24 | 25 | public PandaLockException(String message, Throwable cause) { 26 | super(message, cause); 27 | // TODO Auto-generated constructor stub 28 | } 29 | 30 | public PandaLockException(String message) { 31 | super(message); 32 | // TODO Auto-generated constructor stub 33 | } 34 | 35 | public PandaLockException(Throwable cause) { 36 | super(cause); 37 | // TODO Auto-generated constructor stub 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/jason/pandaLock/core/bean/LockInfo.java: -------------------------------------------------------------------------------- 1 | package com.jason.pandaLock.core.bean; 2 | 3 | import java.util.Date; 4 | import java.util.List; 5 | 6 | import org.apache.zookeeper.CreateMode; 7 | 8 | /** 9 | * @author 作者 E-mail:ruanjianlxm@sina.com 10 | * @version 创建时间:2015年9月11日 下午11:08:15 11 | * 类说明 每个锁的信息 12 | */ 13 | public class LockInfo { 14 | private String lockName;//锁名 15 | private String lockDescription;//锁的描述信息 16 | private CreateMode lockType;//锁的类型 17 | private Date createTime;//锁的创建时间 18 | private List childNodesList;//该锁目前的所有子节点竞争者 19 | public String getLockName() { 20 | return lockName; 21 | } 22 | public void setLockName(String lockName) { 23 | this.lockName = lockName; 24 | } 25 | public String getLockDescription() { 26 | return lockDescription; 27 | } 28 | public void setLockDescription(String lockDescription) { 29 | this.lockDescription = lockDescription; 30 | } 31 | public CreateMode getLockType() { 32 | return lockType; 33 | } 34 | public void setLockType(CreateMode lockType) { 35 | this.lockType = lockType; 36 | } 37 | public Date getCreateTime() { 38 | return createTime; 39 | } 40 | public void setCreateTime(Date createTime) { 41 | this.createTime = createTime; 42 | } 43 | public List getChildNodesList() { 44 | return childNodesList; 45 | } 46 | public void setChildNodesList(List childNodesList) { 47 | this.childNodesList = childNodesList; 48 | } 49 | 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/com/jason/distributedlock/LockTest.java: -------------------------------------------------------------------------------- 1 | package com.jason.distributedlock; 2 | 3 | import java.io.IOException; 4 | 5 | import com.jason.pandaLock.core.exception.PandaLockException; 6 | import com.jason.pandaLock.core.serverImpl.ZkPandaLock; 7 | 8 | /** 9 | * Unit test for simple App. 10 | */ 11 | public class LockTest 12 | { 13 | public static void main(String[] args) throws IOException, InterruptedException, PandaLockException { 14 | //ZkPandaLock zkPandaLock = new ZkPandaLock(); 15 | //zkPandaLock.connectZooKeeper("127.0.0.1:2181", "jason"); 16 | /* boolean tryLockResult = ZkPandaLock.tryLock(); 17 | System.out.println("tryLockResult:"+tryLockResult); 18 | boolean releaseLockResult = ZkPandaLock.releaseLock(); 19 | System.out.println("releaseLockResult:"+releaseLockResult);*/ 20 | for (int i = 0; i < 10; i++) { 21 | new Thread() { 22 | public void run() { 23 | try { 24 | ZkPandaLock zkPandaLock = new ZkPandaLock(); 25 | zkPandaLock.connectZooKeeper("127.0.0.1:2181", "jason"); 26 | zkPandaLock.lock(); 27 | System.out.println(Thread.currentThread().getName()+"在做事,做完就释放锁"); 28 | Thread.sleep(1000); 29 | System.out.println(Thread.currentThread().getName()+"我做完事情了"); 30 | zkPandaLock.releaseLock(); 31 | 32 | } catch (Exception e) { 33 | e.printStackTrace(); 34 | } 35 | } 36 | }.start(); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.jason 6 | distributedLock 7 | 0.0.1 8 | jar 9 | 10 | distributedLock 11 | http://maven.apache.org 12 | 13 | 14 | 15 | 4.10 16 | UTF-8 17 | 1.7 18 | 19 | 20 | 21 | org.apache.zookeeper 22 | zookeeper 23 | 3.4.6 24 | 25 | 26 | org.apache.commons 27 | commons-lang3 28 | 3.4 29 | 30 | 31 | junit 32 | junit 33 | ${junit.version} 34 | test 35 | 36 | 37 | 38 | 39 | 40 | org.apache.maven.plugins 41 | maven-resources-plugin 42 | 43 | UTF-8 44 | 45 | 46 | 47 | 48 | org.apache.maven.plugins 49 | maven-compiler-plugin 50 | 3.3 51 | 52 | 53 | 54 | org.apache.maven.plugins 55 | maven-compiler-plugin 56 | 57 | 1.7 58 | 1.7 59 | UTF-8 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /src/main/java/com/jason/pandaLock/core/bean/CompetitorInfo.java: -------------------------------------------------------------------------------- 1 | package com.jason.pandaLock.core.bean; 2 | /** 3 | * @author 作者 E-mail:ruanjianlxm@sina.com 4 | * @version 创建时间:2015年9月11日 下午11:16:04 5 | * 类说明 每个锁的竞争者节点信息 6 | */ 7 | public class CompetitorInfo implements Comparable { 8 | private String nodeName;//锁竞争者信息 9 | private String nodeDescription;//锁竞争者描述 10 | private Long sequentialId;//该锁信息目前的ID 11 | public String getNodeName() { 12 | return nodeName; 13 | } 14 | public void setNodeName(String nodeName) { 15 | this.nodeName = nodeName; 16 | } 17 | public String getNodeDescription() { 18 | return nodeDescription; 19 | } 20 | public void setNodeDescription(String nodeDescription) { 21 | this.nodeDescription = nodeDescription; 22 | } 23 | public Long getSequentialId() { 24 | return sequentialId; 25 | } 26 | public void setSequentialId( Long sequentialId) { 27 | this.sequentialId = sequentialId; 28 | } 29 | 30 | 31 | @Override 32 | public int hashCode() { 33 | final int prime = 31; 34 | int result = 1; 35 | result = prime * result 36 | + ((nodeDescription == null) ? 0 : nodeDescription.hashCode()); 37 | result = prime * result 38 | + ((nodeName == null) ? 0 : nodeName.hashCode()); 39 | result = prime * result 40 | + ((sequentialId == null) ? 0 : sequentialId.hashCode()); 41 | return result; 42 | } 43 | @Override 44 | public boolean equals(Object obj) { 45 | if (this == obj) 46 | return true; 47 | if (obj == null) 48 | return false; 49 | if (getClass() != obj.getClass()) 50 | return false; 51 | CompetitorInfo other = (CompetitorInfo) obj; 52 | if (nodeDescription == null) { 53 | if (other.nodeDescription != null) 54 | return false; 55 | } else if (!nodeDescription.equals(other.nodeDescription)) 56 | return false; 57 | if (nodeName == null) { 58 | if (other.nodeName != null) 59 | return false; 60 | } else if (!nodeName.equals(other.nodeName)) 61 | return false; 62 | if (sequentialId == null) { 63 | if (other.sequentialId != null) 64 | return false; 65 | } else if (!sequentialId.equals(other.sequentialId)) 66 | return false; 67 | return true; 68 | } 69 | @Override 70 | public int compareTo(Object o) { 71 | // TODO Auto-generated method stub 72 | CompetitorInfo competitorInfo=(CompetitorInfo)o; 73 | 74 | if (nodeName.equals(competitorInfo.getNodeName())) { 75 | return 0 ; 76 | } 77 | if (competitorInfo.getSequentialId()>this.sequentialId) { 78 | return 1; 79 | }else { 80 | return -1; 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/jason/pandaLock/core/serverImpl/ZkPandaLock.java: -------------------------------------------------------------------------------- 1 | package com.jason.pandaLock.core.serverImpl; 2 | 3 | import java.io.IOException; 4 | import java.util.Collections; 5 | import java.util.List; 6 | import java.util.concurrent.CountDownLatch; 7 | 8 | import org.apache.commons.lang3.StringUtils; 9 | import org.apache.zookeeper.CreateMode; 10 | import org.apache.zookeeper.KeeperException; 11 | import org.apache.zookeeper.WatchedEvent; 12 | import org.apache.zookeeper.Watcher; 13 | import org.apache.zookeeper.Watcher.Event.EventType; 14 | import org.apache.zookeeper.Watcher.Event.KeeperState; 15 | import org.apache.zookeeper.ZooDefs.Ids; 16 | import org.apache.zookeeper.ZooKeeper; 17 | import org.apache.zookeeper.data.Stat; 18 | 19 | import com.jason.pandaLock.core.exception.PandaLockException; 20 | import com.jason.pandaLock.core.server.DistributedLock; 21 | 22 | /** 23 | * @author 作者 E-mail:ruanjianlxm@sina.com 24 | * @version 创建时间:2015年10月15日 下午3:30:10 类说明 分布式锁的zk实现 25 | */ 26 | public class ZkPandaLock extends DistributedLock { 27 | /** 28 | * zookeeper节点的默认分隔符 29 | */ 30 | private final static String SEPARATOR = "/"; 31 | /** 32 | * pandaLock在zk中的根节点 33 | */ 34 | private final static String ROOT_PANDALOCK_NODE = SEPARATOR + "parndaLock";// 熊猫锁的zk根节点 35 | /** 36 | * pandaLock默认的EPHEMERAL节点的超时时间,单位毫秒 37 | */ 38 | private static final int DEFAULT_SESSION_TIMEOUT = 5000; 39 | /** 40 | * 竞争者节点,每个想要尝试获得锁的节点都会获得一个竞争者节点 41 | */ 42 | private static final String COMPETITOR_NODE = "competitorNode"; 43 | /** 44 | * 统一的zooKeeper连接,在Init的时候初始化 45 | */ 46 | private static ZooKeeper pandaZk = null; 47 | /** 48 | * 与zk连接成功后消除围栏 49 | */ 50 | private CountDownLatch latch = new CountDownLatch(1); 51 | private CountDownLatch getTheLocklatch = new CountDownLatch(1); 52 | 53 | private String lockName = null; 54 | 55 | private String rootPath = null; 56 | 57 | private String lockPath = null; 58 | 59 | private String competitorPath = null; 60 | 61 | private String thisCompetitorPath = null; 62 | 63 | private String waitCompetitorPath = null; 64 | 65 | @Override 66 | public void releaseLock() { 67 | if (StringUtils.isBlank(rootPath) || StringUtils.isBlank(lockName) 68 | || pandaZk == null) { 69 | throw new PandaLockException( 70 | "you can not release anyLock before you dit not initial connectZookeeper"); 71 | } 72 | try { 73 | 74 | pandaZk.delete(thisCompetitorPath, -1); 75 | 76 | } catch (InterruptedException e) { 77 | // TODO Auto-generated catch block 78 | throw new PandaLockException( 79 | "the release lock has been Interrupted "); 80 | } catch (KeeperException e) { 81 | throw new PandaLockException( 82 | "zookeeper connect error"); 83 | } 84 | 85 | } 86 | 87 | @Override 88 | public boolean tryLock(){ 89 | if (StringUtils.isBlank(rootPath) || StringUtils.isBlank(lockName) 90 | || pandaZk == null) { 91 | throw new PandaLockException( 92 | "you can not tryLock anyone before you dit not initial connectZookeeper"); 93 | } 94 | List allCompetitorList = null; 95 | try { 96 | createComPrtitorNode(); 97 | allCompetitorList = pandaZk.getChildren(lockPath, false); 98 | } catch (KeeperException e) { 99 | throw new PandaLockException("zookeeper connect error"); 100 | } catch (InterruptedException e) { 101 | e.printStackTrace(); 102 | } 103 | Collections.sort(allCompetitorList); 104 | int index = allCompetitorList.indexOf(thisCompetitorPath 105 | .substring((lockPath + SEPARATOR).length())); 106 | if (index == -1) { 107 | throw new PandaLockException("competitorPath not exit after create"); 108 | } else if (index == 0) {// 如果发现自己就是最小节点,那么说明本人获得了锁 109 | return true; 110 | } else {// 说明自己不是最小节点 111 | return false; 112 | } 113 | } 114 | 115 | @Override 116 | public void lock(){ 117 | if (StringUtils.isBlank(rootPath) || StringUtils.isBlank(lockName) 118 | || pandaZk == null) { 119 | throw new PandaLockException( 120 | "you can not lock anyone before you dit not connectZookeeper"); 121 | } 122 | List allCompetitorList = null; 123 | try { 124 | createComPrtitorNode(); 125 | allCompetitorList = pandaZk.getChildren(lockPath, false); 126 | } catch (KeeperException e) { 127 | throw new PandaLockException("zookeeper connect error"); 128 | } catch (InterruptedException e) { 129 | throw new PandaLockException("the lock has been Interrupted"); 130 | } 131 | Collections.sort(allCompetitorList); 132 | int index = allCompetitorList.indexOf(thisCompetitorPath 133 | .substring((lockPath + SEPARATOR).length())); 134 | if (index == -1) { 135 | throw new PandaLockException("competitorPath not exit after create"); 136 | } else if (index == 0) {// 如果发现自己就是最小节点,那么说明本人获得了锁 137 | return; 138 | } else {// 说明自己不是最小节点 139 | waitCompetitorPath = lockPath+SEPARATOR + allCompetitorList.get(index - 1); 140 | // 在waitPath上注册监听器, 当waitPath被删除时, zookeeper会回调监听器的process方法 141 | Stat waitNodeStat; 142 | try { 143 | waitNodeStat = pandaZk.exists(waitCompetitorPath, new Watcher() { 144 | @Override 145 | public void process(WatchedEvent event) { 146 | if (event.getType().equals(EventType.NodeDeleted)&&event.getPath().equals(waitCompetitorPath)) { 147 | getTheLocklatch.countDown(); 148 | } 149 | } 150 | }); 151 | if (waitNodeStat==null) {//如果运行到此处发现前面一个节点已经不存在了。说明前面的进程已经释放了锁 152 | return; 153 | }else { 154 | getTheLocklatch.await(); 155 | return; 156 | } 157 | } catch (KeeperException e) { 158 | throw new PandaLockException("zookeeper connect error"); 159 | } catch (InterruptedException e) { 160 | throw new PandaLockException("the lock has been Interrupted"); 161 | } 162 | 163 | 164 | } 165 | 166 | } 167 | 168 | @Override 169 | public boolean tryLock(int timeout) { 170 | // TODO Auto-generated method stub 171 | return false; 172 | } 173 | 174 | /** 175 | * 创建竞争者节点 176 | * 177 | * @throws KeeperException 178 | * @throws InterruptedException 179 | */ 180 | private void createComPrtitorNode() throws KeeperException, 181 | InterruptedException { 182 | competitorPath = lockPath + SEPARATOR + COMPETITOR_NODE; 183 | thisCompetitorPath = pandaZk.create(competitorPath, null, 184 | Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); 185 | } 186 | 187 | public void connectZooKeeper(String zkhosts, String lockName) 188 | throws KeeperException, InterruptedException, 189 | IOException { 190 | Stat rootStat = null; 191 | Stat lockStat = null; 192 | if (StringUtils.isBlank(zkhosts)) { 193 | throw new PandaLockException("zookeeper hosts can not be blank"); 194 | } 195 | if (StringUtils.isBlank(lockName)) { 196 | throw new PandaLockException("lockName can not be blank"); 197 | } 198 | if (pandaZk == null) { 199 | pandaZk = new ZooKeeper(zkhosts, DEFAULT_SESSION_TIMEOUT, 200 | new Watcher() { 201 | 202 | @Override 203 | public void process(WatchedEvent event) { 204 | if (event.getState().equals( 205 | KeeperState.SyncConnected)) { 206 | latch.countDown(); 207 | } 208 | 209 | } 210 | }); 211 | } 212 | latch.await(); 213 | rootStat = pandaZk.exists(ROOT_PANDALOCK_NODE, false); 214 | if (rootStat == null) { 215 | rootPath = pandaZk.create(ROOT_PANDALOCK_NODE, null, 216 | Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); 217 | } else { 218 | rootPath = ROOT_PANDALOCK_NODE; 219 | } 220 | String lockNodePathString = ROOT_PANDALOCK_NODE + SEPARATOR + lockName; 221 | lockStat = pandaZk.exists(lockNodePathString, false); 222 | if (lockStat != null) {// 说明此锁已经存在 223 | lockPath = lockNodePathString; 224 | // throw new 225 | // PandaLockException("the lockName is already exits in zookeeper, please check the lockName :" 226 | // +lockName); 227 | } else { 228 | // 创建相对应的锁节点 229 | lockPath = pandaZk.create(lockNodePathString, null, 230 | Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); 231 | } 232 | 233 | this.lockName = lockName; 234 | } 235 | 236 | 237 | } 238 | --------------------------------------------------------------------------------