├── .gitignore ├── README.md ├── src └── main │ ├── webapp │ ├── index.jsp │ └── WEB-INF │ │ └── web.xml │ └── java │ ├── redis │ ├── ThreadA.java │ ├── Test.java │ ├── Service.java │ └── DistributedLock.java │ └── zookeeper │ ├── Test.java │ └── DistributedLock.java └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | /target/* 2 | /.idea/* 3 | /*.iml -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # **分布式锁的简单实现** 2 | 3 | ## **基于Redis实现分布式锁** 4 | 5 | ## **基于ZooKeeper实现分布式锁** -------------------------------------------------------------------------------- /src/main/webapp/index.jsp: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Hello World!

4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | Archetype Created Web Application 7 | 8 | -------------------------------------------------------------------------------- /src/main/java/redis/ThreadA.java: -------------------------------------------------------------------------------- 1 | package redis; 2 | 3 | /** 4 | * Created by liuyang on 2017/4/20. 5 | */ 6 | public class ThreadA extends Thread { 7 | private Service service; 8 | 9 | public ThreadA(Service service) { 10 | this.service = service; 11 | } 12 | 13 | @Override 14 | public void run() { 15 | service.seckill(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/redis/Test.java: -------------------------------------------------------------------------------- 1 | package redis; 2 | 3 | /** 4 | * Created by liuyang on 2017/4/20. 5 | */ 6 | public class Test { 7 | public static void main(String[] args) { 8 | Service service = new Service(); 9 | for (int i = 0; i < 50; i++) { 10 | ThreadA threadA = new ThreadA(service); 11 | threadA.start(); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/zookeeper/Test.java: -------------------------------------------------------------------------------- 1 | package zookeeper; 2 | 3 | /** 4 | * Created by liuyang on 2017/4/20. 5 | */ 6 | public class Test { 7 | static int n = 500; 8 | 9 | public static void secskill() { 10 | System.out.println(--n); 11 | } 12 | 13 | public static void main(String[] args) { 14 | 15 | 16 | Runnable runnable = new Runnable() { 17 | public void run() { 18 | DistributedLock lock = null; 19 | try { 20 | lock = new DistributedLock("127.0.0.1:2181", "test1"); 21 | lock.lock(); 22 | secskill(); 23 | System.out.println(Thread.currentThread().getName() + "正在运行"); 24 | } finally { 25 | if (lock != null) { 26 | lock.unlock(); 27 | } 28 | } 29 | } 30 | }; 31 | 32 | for (int i = 0; i < 10; i++) { 33 | Thread t = new Thread(runnable); 34 | t.start(); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/redis/Service.java: -------------------------------------------------------------------------------- 1 | package redis; 2 | 3 | import redis.clients.jedis.JedisPool; 4 | import redis.clients.jedis.JedisPoolConfig; 5 | 6 | /** 7 | * Created by liuyang on 2017/4/20. 8 | */ 9 | public class Service { 10 | private static JedisPool pool = null; 11 | 12 | static { 13 | JedisPoolConfig config = new JedisPoolConfig(); 14 | // 设置最大连接数 15 | config.setMaxTotal(200); 16 | // 设置最大空闲数 17 | config.setMaxIdle(8); 18 | // 设置最大等待时间 19 | config.setMaxWaitMillis(1000 * 100); 20 | // 在borrow一个jedis实例时,是否需要验证,若为true,则所有jedis实例均是可用的 21 | config.setTestOnBorrow(true); 22 | pool = new JedisPool(config, "127.0.0.1", 6379, 3000); 23 | } 24 | 25 | DistributedLock lock = new DistributedLock(pool); 26 | 27 | int n = 500; 28 | 29 | public void seckill() { 30 | // 返回锁的value值,供释放锁时候进行判断 31 | String indentifier = lock.lockWithTimeout("resource", 5000, 1000); 32 | System.out.println(Thread.currentThread().getName() + "获得了锁"); 33 | System.out.println(--n); 34 | lock.releaseLock("resource", indentifier); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | cn.liu5599 5 | distributedlock 6 | war 7 | 1.0-SNAPSHOT 8 | distributedlock Maven Webapp 9 | http://maven.apache.org 10 | 11 | 12 | junit 13 | junit 14 | 3.8.1 15 | test 16 | 17 | 18 | 19 | 20 | redis.clients 21 | jedis 22 | 2.9.0 23 | 24 | 25 | 26 | 27 | org.apache.zookeeper 28 | zookeeper 29 | 3.4.9 30 | 31 | 32 | 33 | 34 | 35 | distributedlock 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/main/java/redis/DistributedLock.java: -------------------------------------------------------------------------------- 1 | package redis; 2 | 3 | import redis.clients.jedis.Jedis; 4 | import redis.clients.jedis.JedisPool; 5 | import redis.clients.jedis.Transaction; 6 | import redis.clients.jedis.exceptions.JedisException; 7 | 8 | import java.util.List; 9 | import java.util.UUID; 10 | 11 | /** 12 | * Created by liuyang on 2017/4/20. 13 | */ 14 | public class DistributedLock { 15 | private final JedisPool jedisPool; 16 | 17 | public DistributedLock(JedisPool jedisPool) { 18 | this.jedisPool = jedisPool; 19 | } 20 | 21 | /** 22 | * 加锁 23 | * @param locaName 锁的key 24 | * @param acquireTimeout 获取超时时间 25 | * @param timeout 锁的超时时间 26 | * @return 锁标识 27 | */ 28 | public String lockWithTimeout(String locaName, 29 | long acquireTimeout, long timeout) { 30 | Jedis conn = null; 31 | String retIdentifier = null; 32 | try { 33 | // 获取连接 34 | conn = jedisPool.getResource(); 35 | // 随机生成一个value 36 | String identifier = UUID.randomUUID().toString(); 37 | // 锁名,即key值 38 | String lockKey = "lock:" + locaName; 39 | // 超时时间,上锁后超过此时间则自动释放锁 40 | int lockExpire = (int)(timeout / 1000); 41 | 42 | // 获取锁的超时时间,超过这个时间则放弃获取锁 43 | long end = System.currentTimeMillis() + acquireTimeout; 44 | while (System.currentTimeMillis() < end) { 45 | if (conn.setnx(lockKey, identifier) == 1) { 46 | conn.expire(lockKey, lockExpire); 47 | // 返回value值,用于释放锁时间确认 48 | retIdentifier = identifier; 49 | return retIdentifier; 50 | } 51 | // 返回-1代表key没有设置超时时间,为key设置一个超时时间 52 | if (conn.ttl(lockKey) == -1) { 53 | conn.expire(lockKey, lockExpire); 54 | } 55 | 56 | try { 57 | Thread.sleep(10); 58 | } catch (InterruptedException e) { 59 | Thread.currentThread().interrupt(); 60 | } 61 | } 62 | } catch (JedisException e) { 63 | e.printStackTrace(); 64 | } finally { 65 | if (conn != null) { 66 | conn.close(); 67 | } 68 | } 69 | return retIdentifier; 70 | } 71 | 72 | /** 73 | * 释放锁 74 | * @param lockName 锁的key 75 | * @param identifier 释放锁的标识 76 | * @return 77 | */ 78 | public boolean releaseLock(String lockName, String identifier) { 79 | Jedis conn = null; 80 | String lockKey = "lock:" + lockName; 81 | boolean retFlag = false; 82 | try { 83 | conn = jedisPool.getResource(); 84 | while (true) { 85 | // 监视lock,准备开始事务 86 | conn.watch(lockKey); 87 | // 通过前面返回的value值判断是不是该锁,若是该锁,则删除,释放锁 88 | if (identifier.equals(conn.get(lockKey))) { 89 | Transaction transaction = conn.multi(); 90 | transaction.del(lockKey); 91 | List results = transaction.exec(); 92 | if (results == null) { 93 | continue; 94 | } 95 | retFlag = true; 96 | } 97 | conn.unwatch(); 98 | break; 99 | } 100 | } catch (JedisException e) { 101 | e.printStackTrace(); 102 | } finally { 103 | if (conn != null) { 104 | conn.close(); 105 | } 106 | } 107 | return retFlag; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/zookeeper/DistributedLock.java: -------------------------------------------------------------------------------- 1 | package zookeeper; 2 | 3 | import org.apache.zookeeper.*; 4 | import org.apache.zookeeper.data.Stat; 5 | 6 | import java.io.IOException; 7 | import java.util.ArrayList; 8 | import java.util.Collections; 9 | import java.util.List; 10 | import java.util.concurrent.CountDownLatch; 11 | import java.util.concurrent.TimeUnit; 12 | import java.util.concurrent.locks.Condition; 13 | import java.util.concurrent.locks.Lock; 14 | 15 | /** 16 | * Created by liuyang on 2017/4/20. 17 | */ 18 | public class DistributedLock implements Lock, Watcher { 19 | private ZooKeeper zk = null; 20 | // 根节点 21 | private String ROOT_LOCK = "/locks"; 22 | // 竞争的资源 23 | private String lockName; 24 | // 等待的前一个锁 25 | private String WAIT_LOCK; 26 | // 当前锁 27 | private String CURRENT_LOCK; 28 | // 计数器 29 | private CountDownLatch countDownLatch; 30 | private int sessionTimeout = 30000; 31 | private List exceptionList = new ArrayList(); 32 | 33 | /** 34 | * 配置分布式锁 35 | * @param config 连接的url 36 | * @param lockName 竞争资源 37 | */ 38 | public DistributedLock(String config, String lockName) { 39 | this.lockName = lockName; 40 | try { 41 | // 连接zookeeper 42 | zk = new ZooKeeper(config, sessionTimeout, this); 43 | Stat stat = zk.exists(ROOT_LOCK, false); 44 | if (stat == null) { 45 | // 如果根节点不存在,则创建根节点 46 | zk.create(ROOT_LOCK, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); 47 | } 48 | } catch (IOException e) { 49 | e.printStackTrace(); 50 | } catch (InterruptedException e) { 51 | e.printStackTrace(); 52 | } catch (KeeperException e) { 53 | e.printStackTrace(); 54 | } 55 | } 56 | 57 | // 节点监视器 58 | public void process(WatchedEvent event) { 59 | if (this.countDownLatch != null) { 60 | this.countDownLatch.countDown(); 61 | } 62 | } 63 | 64 | public void lock() { 65 | if (exceptionList.size() > 0) { 66 | throw new LockException(exceptionList.get(0)); 67 | } 68 | try { 69 | if (this.tryLock()) { 70 | System.out.println(Thread.currentThread().getName() + " " + lockName + "获得了锁"); 71 | return; 72 | } else { 73 | // 等待锁 74 | waitForLock(WAIT_LOCK, sessionTimeout); 75 | } 76 | } catch (InterruptedException e) { 77 | e.printStackTrace(); 78 | } catch (KeeperException e) { 79 | e.printStackTrace(); 80 | } 81 | } 82 | 83 | public boolean tryLock() { 84 | try { 85 | String splitStr = "_lock_"; 86 | if (lockName.contains(splitStr)) { 87 | throw new LockException("锁名有误"); 88 | } 89 | // 创建临时有序节点 90 | CURRENT_LOCK = zk.create(ROOT_LOCK + "/" + lockName + splitStr, new byte[0], 91 | ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); 92 | System.out.println(CURRENT_LOCK + " 已经创建"); 93 | // 取所有子节点 94 | List subNodes = zk.getChildren(ROOT_LOCK, false); 95 | // 取出所有lockName的锁 96 | List lockObjects = new ArrayList(); 97 | for (String node : subNodes) { 98 | String _node = node.split(splitStr)[0]; 99 | if (_node.equals(lockName)) { 100 | lockObjects.add(node); 101 | } 102 | } 103 | Collections.sort(lockObjects); 104 | System.out.println(Thread.currentThread().getName() + " 的锁是 " + CURRENT_LOCK); 105 | // 若当前节点为最小节点,则获取锁成功 106 | if (CURRENT_LOCK.equals(ROOT_LOCK + "/" + lockObjects.get(0))) { 107 | return true; 108 | } 109 | 110 | // 若不是最小节点,则找到自己的前一个节点 111 | String prevNode = CURRENT_LOCK.substring(CURRENT_LOCK.lastIndexOf("/") + 1); 112 | WAIT_LOCK = lockObjects.get(Collections.binarySearch(lockObjects, prevNode) - 1); 113 | } catch (InterruptedException e) { 114 | e.printStackTrace(); 115 | } catch (KeeperException e) { 116 | e.printStackTrace(); 117 | } 118 | return false; 119 | } 120 | 121 | public boolean tryLock(long timeout, TimeUnit unit) { 122 | try { 123 | if (this.tryLock()) { 124 | return true; 125 | } 126 | return waitForLock(WAIT_LOCK, timeout); 127 | } catch (Exception e) { 128 | e.printStackTrace(); 129 | } 130 | return false; 131 | } 132 | 133 | // 等待锁 134 | private boolean waitForLock(String prev, long waitTime) throws KeeperException, InterruptedException { 135 | Stat stat = zk.exists(ROOT_LOCK + "/" + prev, true); 136 | 137 | if (stat != null) { 138 | System.out.println(Thread.currentThread().getName() + "等待锁 " + ROOT_LOCK + "/" + prev); 139 | this.countDownLatch = new CountDownLatch(1); 140 | // 计数等待,若等到前一个节点消失,则precess中进行countDown,停止等待,获取锁 141 | this.countDownLatch.await(waitTime, TimeUnit.MILLISECONDS); 142 | this.countDownLatch = null; 143 | System.out.println(Thread.currentThread().getName() + " 等到了锁"); 144 | } 145 | return true; 146 | } 147 | 148 | public void unlock() { 149 | try { 150 | System.out.println("释放锁 " + CURRENT_LOCK); 151 | zk.delete(CURRENT_LOCK, -1); 152 | CURRENT_LOCK = null; 153 | zk.close(); 154 | } catch (InterruptedException e) { 155 | e.printStackTrace(); 156 | } catch (KeeperException e) { 157 | e.printStackTrace(); 158 | } 159 | } 160 | 161 | public Condition newCondition() { 162 | return null; 163 | } 164 | 165 | public void lockInterruptibly() throws InterruptedException { 166 | this.lock(); 167 | } 168 | 169 | 170 | public class LockException extends RuntimeException { 171 | private static final long serialVersionUID = 1L; 172 | public LockException(String e){ 173 | super(e); 174 | } 175 | public LockException(Exception e){ 176 | super(e); 177 | } 178 | } 179 | } 180 | --------------------------------------------------------------------------------