两种分布式锁实现方案二(14)

四。方案2,基于Redis的分布式锁

  1. /** 
  2.  * 分布式锁工厂类 
  3.  */  
  4. public class RedisLockUtil {  
  5.     private static final Logger logger = Logger.getLogger(RedisLockUtil.class);  
  6.     private static Object schemeLock = new Object();  
  7.     private static Map<String, RedisLockUtil> instances = new ConcurrentHashMap();  
  8.   
  9.     public static RedisLockUtil getInstance(String schema) {  
  10.         RedisLockUtil u = instances.get(schema);  
  11.         if (u == null) {  
  12.             synchronized (schemeLock) {  
  13.                 u = instances.get(schema);  
  14.                 if (u == null) {  
  15.                     LockObserver lo = new LockObserver(schema);  
  16.                     u = new RedisLockUtil(schema, lo);  
  17.                     instances.put(schema, u);  
  18.                 }  
  19.             }  
  20.         }  
  21.         return u;  
  22.     }  
  23.   
  24.     private Object mutexLock = new Object();  
  25.     private Map<String, Object> mutexLockMap = new ConcurrentHashMap();  
  26.     private Map<String, RedisReentrantLock> cache = new ConcurrentHashMap<String, RedisReentrantLock>();  
  27.     private DelayQueue<RedisReentrantLock> dq = new DelayQueue<RedisReentrantLock>();  
  28.     private AbstractLockObserver lo;  
  29.   
  30.     public RedisLockUtil(String schema, AbstractLockObserver lo) {  
  31.         Thread th = new Thread(lo);  
  32.         th.setDaemon(false);  
  33.         th.setName("Lock Observer:"schema);  
  34.         th.start();  
  35.         clearUselessLocks(schema);  
  36.         this.lo = lo;  
  37.     }  
  38.   
  39.     public void clearUselessLocks(String schema) {  
  40.         Thread th = new Thread(new Runnable() {  
  41.             @Override  
  42.             public void run() {  
  43.                 while (!SystemExitListener.isOver()) {  
  44.                     try {  
  45.                         RedisReentrantLock t = dq.take();  
  46.                         if (t.clear()) {  
  47.                             String key = t.getKey();  
  48.                             synchronized (getMutex(key)) {  
  49.                                 cache.remove(key);  
  50.                             }  
  51.                         }  
  52.                         t.resetCleartime();  
  53.                     } catch (InterruptedException e) {  
  54.                     }  
  55.                 }  
  56.             }  
  57.   
  58.         });  
  59.         th.setDaemon(true);  
  60.         th.setName("Lock cleaner:"schema);  
  61.         th.start();  
  62.     }  
  63.   
  64.     private Object getMutex(String key) {  
  65.         Object mx = mutexLockMap.get(key);  
  66.         if (mx == null) {  
  67.             synchronized (mutexLock) {  
  68.                 mx = mutexLockMap.get(key);  
  69.                 if (mx == null) {  
  70.                     mx = new Object();  
  71.                     mutexLockMap.put(key, mx);  
  72.                 }  
  73.             }  
  74.         }  
  75.         return mx;  
  76.     }  
  77.   
  78.     private RedisReentrantLock getLock(String key, boolean addref) {  
  79.         RedisReentrantLock lock = cache.get(key);  
  80.         if (lock == null) {  
  81.             synchronized (getMutex(key)) {  
  82.                 lock = cache.get(key);  
  83.                 if (lock == null) {  
  84.                     lock = new RedisReentrantLock(key, lo);  
  85.                     cache.put(key, lock);  
  86.                 }  
  87.             }  
  88.         }  
  89.         if (addref) {  
  90.             if (!lock.incRef()) {  
  91.                 synchronized (getMutex(key)) {  
  92.                     lock = cache.get(key);  
  93.                     if (!lock.incRef()) {  
  94.                         lock = new RedisReentrantLock(key, lo);  
  95.                         cache.put(key, lock);  
  96.                     }  
  97.                 }  
  98.             }  
  99.         }  
  100.         return lock;  
  101.     }  
  102.   
  103.     public void reset() {  
  104.         for (String s : cache.keySet()) {  
  105.             getLock(s, false).unlock();  
  106.         }  
  107.     }  
  108.   
  109.     /** 
  110.      * 尝试加锁 
  111.      * 如果当前线程已经拥有该锁的话,直接返回,表示不用再次加锁,此时不应该再调用unlock进行解锁 
  112.      * 
  113.      * @param key 
  114.      * @return 
  115.      * @throws Exception 
  116.      * @throws InterruptedException 
  117.      * @throws KeeperException 
  118.      */  
  119.     public LockStat lock(String key) {  
  120.         return lock(key, -1);  
  121.     }  
  122.   
  123.     public LockStat lock(String key, int timeout) {  
  124.         RedisReentrantLock ll = getLock(key, true);  
  125.         ll.incRef();  
  126.         try {  
  127.             if (ll.isOwner(false)) {  
  128.                 ll.descrRef();  
  129.                 return LockStat.NONEED;  
  130.             }  
  131.             if (ll.lock(timeout)) {  
  132.                 return LockStat.SUCCESS;  
  133.             } else {  
  134.                 ll.descrRef();  
  135.                 if (ll.setCleartime()) {  
  136.                     dq.put(ll);  
  137.                 }  
  138.                 return null;  
  139.             }  
  140.         } catch (LockNotExistsException e) {  
  141.             ll.descrRef();  
  142.             return lock(key, timeout);  
  143.         } catch (RuntimeException e) {  
  144.             ll.descrRef();  
  145.             throw e;  
  146.         }  
  147.     }  
  148.   
  149.     public void unlock(String key, LockStat stat) {  
  150.         unlock(key, stat, false);  
  151.     }  
  152.   
  153.     public void unlock(String key, LockStat stat, boolean keepalive) {  
  154.         if (stat == nullreturn;  
  155.         if (LockStat.SUCCESS.equals(stat)) {  
  156.             RedisReentrantLock lock = getLock(key, false);  
  157.             boolean candestroy = lock.unlock();  
  158.             if (candestroy && !keepalive) {  
  159.                 if (lock.setCleartime()) {  
  160.                     dq.put(lock);  
  161.                 }  
  162.             }  
  163.         }  
  164.     }  
  165.   
  166.     public static enum LockStat {  
  167.         NONEED,  
  168.         SUCCESS  
  169.     }  
  170. }  
  1. /** 
  2.  * 分布式锁本地代理类 
  3.  */  
  4. public class RedisReentrantLock implements Delayed {  
  5.     private static final Logger logger = Logger.getLogger(RedisReentrantLock.class);  
  6.     private ReentrantLock reentrantLock = new ReentrantLock();  
  7.   
  8.     private RedisLock redisLock;  
  9.     private long timeout = 3 * 60;  
  10.     private CountDownLatch lockcount = new CountDownLatch(1);  
  11.   
  12.     private String key;  
  13.     private AbstractLockObserver observer;  
  14.   
  15.     private int ref = 0;  
  16.     private Object refLock = new Object();  
  17.     private boolean destroyed = false;  
  18.   
  19.     private long cleartime = -1;  
  20.   
  21.     public RedisReentrantLock(String key, AbstractLockObserver observer) {  
  22.         this.key = key;  
  23.         this.observer = observer;  
  24.         initWriteLock();  
  25.     }  
  26.   
  27.     public boolean isDestroyed() {  
  28.         return destroyed;  
  29.     }  
  30.   
  31.     private synchronized void initWriteLock() {  
  32.         redisLock = new RedisLock(key, new LockListener() {  
  33.             @Override  
  34.             public void lockAcquired() {  
  35.                 lockcount.countDown();  
  36.             }  
  37.   
  38.             @Override  
  39.             public long getExpire() {  
  40.                 return 0;  
  41.             }  
  42.   
  43.             @Override  
  44.             public void lockError() {  
  45.                 /*synchronized(mutex){ 
  46.                     mutex.notify(); 
  47.                 }*/  
  48.                 lockcount.countDown();  
  49.             }  
  50.         }, observer);  
  51.     }  
  52.   
  53.     public boolean incRef() {  
  54.         synchronized (refLock) {  
  55.             if (destroyed) return false;  
  56.             ref;  
  57.         }  
  58.         return true;  
  59.     }  
  60.   
  61.     public void descrRef() {  
  62.         synchronized (refLock) {  
  63.             ref--;  
  64.         }  
  65.     }  
  66.   
  67.     public boolean clear() {  
  68.         if (destroyed) return true;  
  69.         synchronized (refLock) {  
  70.             if (ref > 0) {  
  71.                 return false;  
  72.             }  
  73.             destroyed = true;  
  74.             redisLock.clear();  
  75.             redisLock = null;  
  76.             return true;  
  77.         }  
  78.     }  
  79.   
  80.     public boolean lock(long timeout) throws LockNotExistsException {  
  81.         if (timeout <= 0) timeout = this.timeout;  
  82.         //incRef();  
  83.         reentrantLock.lock();//多线程竞争时,先拿到第一层锁  
  84.         if (redisLock == null) {  
  85.             reentrantLock.unlock();  
  86.             //descrRef();  
  87.             throw new LockNotExistsException();  
  88.         }  
  89.         try {  
  90.             lockcount = new CountDownLatch(1);  
  91.             boolean res = redisLock.trylock(timeout);  
  92.             if (!res) {  
  93.                 lockcount.await(timeout, TimeUnit.SECONDS);  
  94.                 //mutex.wait(timeout*1000);  
  95.                 if (!redisLock.doExpire()) {  
  96.                     reentrantLock.unlock();  
  97.                     return false;  
  98.                 }  
  99.             }  
  100.             return true;  
  101.         } catch (InterruptedException e) {  
  102.             reentrantLock.unlock();  
  103.             return false;  
  104.         }  
  105.     }  
  106.   
  107.     public boolean lock() throws LockNotExistsException {  
  108.         return lock(timeout);  
  109.     }  
  110.   
  111.     public boolean unlock() {  
  112.         if (!isOwner(true)) {  
  113.             try {  
  114.                 throw new RuntimeException("big ================================================ error.key:"key);  
  115.             } catch (Exception e) {  
  116.                 logger.error("err:"e, e);  
  117.             }  
  118.             return false;  
  119.         }  
  120.         try {  
  121.             redisLock.unlock();  
  122.             reentrantLock.unlock();//多线程竞争时,释放最外层锁  
  123.         } catch (RuntimeException e) {  
  124.             reentrantLock.unlock();//多线程竞争时,释放最外层锁  
  125.             throw e;  
  126.         } finally {  
  127.             descrRef();  
  128.         }  
  129.         return canDestroy();  
  130.     }  
  131.   
  132.     public boolean canDestroy() {  
  133.         synchronized (refLock) {  
  134.             return ref <= 0;  
  135.         }  
  136.     }  
  137.   
  138.     <a href="http://www.nbso.ca/">  
  139.     nbso online  
  140.     casino reviews  
  141.     </a>  
  142.   
  143.     public String getKey() {  
  144.         return key;  
  145.     }  
  146.   
  147.     public void setKey(String key) {  
  148.         this.key = key;  
  149.     }  
  150.   
  151.     public boolean isOwner(boolean check) {  
  152.         synchronized (refLock) {  
  153.             if (redisLock == null) {  
  154.                 logger.error("reidsLock is null:key="key);  
  155.                 return false;  
  156.             }  
  157.             boolean a = reentrantLock.isHeldByCurrentThread();  
  158.             boolean b = redisLock.isOwner();  
  159.             if (check) {  
  160.                 if (!a || !b) {  
  161.                     logger.error(key";a:"a";b:"b);  
  162.                 }  
  163.             }  
  164.             return a && b;  
  165.         }  
  166.     }  
  167.   
  168.     public boolean setCleartime() {  
  169.         synchronized (this) {  
  170.             if (cleartime > 0return false;  
  171.             this.cleartime = System.currentTimeMillis() 10 * 1000;  
  172.             return true;  
  173.         }  
  174.     }  
  175.   
  176.     public void resetCleartime() {  
  177.         synchronized (this) {  
  178.             this.cleartime = -1;  
  179.         }  
  180.     }  
  181.   
  182.     @Override  
  183.     public int compareTo(Delayed object) {  
  184.         if (object instanceof RedisReentrantLock) {  
  185.             RedisReentrantLock t = (RedisReentrantLock) object;  
  186.             long l = this.cleartime - t.cleartime;  
  187.   
  188.             if (l > 0return 1//比当前的小则返回1,比当前的大则返回-1,否则为0  
  189.             else if (l < 0return -1;  
  190.             else return 0;  
  191.         }  
  192.         return 0;  
  193.     }  
  194.   
  195.     @Override  
  196.     public long getDelay(TimeUnit unit) {  
  197.         long d = unit.convert(cleartime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);  
  198.         return d;  
  199.     }  
  200.   
  201. }  
  1. /** 
  2.  * 使用Redis实现的分布式锁 
  3.  * 基本工作原理如下: 
  4.  * 1. 使用setnx(key,时间戮 超时),如果设置成功,则直接拿到锁 
  5.  * 2. 如果设置不成功,获取key的值v1(它的到期时间戮),跟当前时间对比,看是否已经超时 
  6.  * 3. 如果超时(说明拿到锁的结点已经挂掉),v2=getset(key,时间戮 超时 1),判断v2是否等于v1,如果相等,加锁成功,否则加锁失败,等过段时间再重试(200MS) 
  7.  */  
  8. public class RedisLock implements LockListener {  
  9.     private String key;  
  10.     private boolean owner = false;  
  11.     private AbstractLockObserver observer = null;  
  12.     private LockListener lockListener = null;  
  13.     private boolean waiting = false;  
  14.     private long expire;//锁超时时间,以秒为单位  
  15.     private boolean expired = false;  
  16.   
  17.     public RedisLock(String key, LockListener lockListener, AbstractLockObserver observer) {  
  18.         this.key = key;  
  19.         this.lockListener = lockListener;  
  20.         this.observer = observer;  
  21.     }  
  22.   
  23.     public boolean trylock(long expire) {  
  24.         synchronized (this) {  
  25.             if (owner) {  
  26.                 return true;  
  27.             }  
  28.             this.expire = expire;  
  29.             this.expired = false;  
  30.             if (!waiting) {  
  31.                 owner = observer.tryLock(key, expire);  
  32.                 if (!owner) {  
  33.                     waiting = true;  
  34.                     observer.addLockListener(key, this);  
  35.                 }  
  36.             }  
  37.             return owner;  
  38.         }  
  39.     }  
  40.   
  41.     public boolean isOwner() {  
  42.         return owner;  
  43.     }  
  44.   
  45.     public void unlock() {  
  46.         synchronized (this) {  
  47.             observer.unLock(key);  
  48.             owner = false;  
  49.         }  
  50.     }  
  51.   
  52.     public void clear() {  
  53.         synchronized (this) {  
  54.             if (waiting) {  
  55.                 observer.removeLockListener(key);  
  56.                 waiting = false;  
  57.             }  
  58.         }  
  59.     }  
  60.   
  61.     public boolean doExpire() {  
  62.         synchronized (this) {  
  63.             if (owner) return true;  
  64.             if (expired) return false;  
  65.             expired = true;  
  66.             clear();  
  67.         }  
  68.         return false;  
  69.     }  
  70.   
  71.     @Override  
  72.     public void lockAcquired() {  
  73.         synchronized (this) {  
  74.             if (expired) {  
  75.                 unlock();  
  76.                 return;  
  77.             }  
  78.             owner = true;  
  79.             waiting = false;  
  80.         }  
  81.         lockListener.lockAcquired();  
  82.     }  
  83.   
  84.     @Override  
  85.     public long getExpire() {  
  86.         return this.expire;  
  87.     }  
  88.   
  89.     @Override  
  90.     public void lockError() {  
  91.         synchronized (this) {  
  92.             owner = false;  
  93.             waiting = false;  
  94.             lockListener.lockError();  
  95.         }  
  96.     }  
  97.   
  98. }  


  1. public class LockObserver extends AbstractLockObserver implements Runnable {  
  2.     private CacheRedisClient client;  
  3.     private Object mutex = new Object();  
  4.     private Map<String, LockListener> lockMap = new ConcurrentHashMap();  
  5.     private boolean stoped = false;  
  6.     private long interval = 500;  
  7.     private boolean terminated = false;  
  8.     private CountDownLatch doneSignal = new CountDownLatch(1);  
  9.   
  10.     public LockObserver(String schema) {  
  11.         client = new CacheRedisClient(schema);  
  12.   
  13.         SystemExitListener.addTerminateListener(new ExitHandler() {  
  14.             public void run() {  
  15.                 stoped = true;  
  16.                 try {  
  17.                     doneSignal.await();  
  18.                 } catch (InterruptedException e) {  
  19.                 }  
  20.             }  
  21.         });  
  22.     }  
  23.   
  24.   
  25.     public void addLockListener(String key, LockListener listener) {  
  26.         if (terminated) {  
  27.             listener.lockError();  
  28.             return;  
  29.         }  
  30.         synchronized (mutex) {  
  31.             lockMap.put(key, listener);  
  32.         }  
  33.     }  
  34.   
  35.     public void removeLockListener(String key) {  
  36.         synchronized (mutex) {  
  37.             lockMap.remove(key);  
  38.         }  
  39.     }  
  40.   
  41.     @Override  
  42.     public void run() {  
  43.         while (!terminated) {  
  44.             long p1 = System.currentTimeMillis();  
  45.             Map<String, LockListener> clone = new HashMap();  
  46.             synchronized (mutex) {  
  47.                 clone.putAll(lockMap);  
  48.             }  
  49.             Set<String> keyset = clone.keySet();  
  50.             if (keyset.size() > 0) {  
  51.                 ConnectionFactory.setSingleConnectionPerThread(keyset.size());  
  52.                 for (String key : keyset) {  
  53.                     LockListener ll = clone.get(key);  
  54.                     try {  
  55.                         if (tryLock(key, ll.getExpire())) {  
  56.                             ll.lockAcquired();  
  57.                             removeLockListener(key);  
  58.                         }  
  59.                     } catch (Exception e) {  
  60.                         ll.lockError();  
  61.                         removeLockListener(key);  
  62.                     }  
  63.                 }  
  64.                 ConnectionFactory.releaseThreadConnection();  
  65.             } else {  
  66.                 if (stoped) {  
  67.                     terminated = true;  
  68.                     doneSignal.countDown();  
  69.                     return;  
  70.                 }  
  71.             }  
  72.             try {  
  73.                 long p2 = System.currentTimeMillis();  
  74.                 long cost = p2 - p1;  
  75.                 if (cost <= interval) {  
  76.                     Thread.sleep(interval - cost);  
  77.                 } else {  
  78.                     Thread.sleep(interval * 2);  
  79.                 }  
  80.             } catch (InterruptedException e) {  
  81.             }  
  82.         }  
  83.   
  84.     }  
  85.   
  86.   
  87.     /** 
  88.      * 超时时间单位为s!!! 
  89.      * 
  90.      * @param key 
  91.      * @param expire 
  92.      * @return 
  93.      */  
  94.     public boolean tryLock(final String key, final long expireInSecond) {  
  95.         if (terminated) return false;  
  96.         final long tt = System.currentTimeMillis();  
  97.         final long expire = expireInSecond * 1000;  
  98.         final Long ne = tt expire;  
  99.         List<Object> mm = client.multi(key, new MultiBlock() {  
  100.             @Override  
  101.             public void execute() {  
  102.                 transaction.setnxObject(key, ne);  
  103.                 transaction.get(SafeEncoder.encode(key));  
  104.             }  
  105.         });  
  106.         Long res = (Long) mm.get(0);  
  107.         if (new Long(1).equals(res)) {  
  108.             return true;  
  109.         } else {  
  110.             byte[] bb = (byte[]) mm.get(1);  
  111.             Long ex = client.deserialize(bb);  
  112.             if (ex == null || tt > ex) {  
  113.                 Long old = client.getSet(key, new Long(ne 1));  
  114.                 if (old == null || (ex == null && old == null) || (ex != null && ex.equals(old))) {  
  115.                     return true;  
  116.                 }  
  117.             }  
  118.         }  
  119.         return false;  
  120.     }  
  121.   
  122.     public void unLock(String key) {  
  123.         client.del(key);  
  124.     }  
  125. }  



使用本方案实现的分布式锁,可以完美地解决锁重入问题;通过引入超时也避免了死锁问题;性能方面,笔者自测试结果如下:


500线程 tps = 35000
[root@DB1 benchtest-util]# target/benchtest/bin/TestFastRedis /data/config/util/config_0_11.properties lock 500 500000
线程总时间:6553466;平均:13.106932
实际总时间:13609; 平均:0.027218

TPS达到35000,比方案1强了整整一个数量级;

五。总结
本文介绍了两种分布式锁的实现方案;方案1最大的优势在于避免结点挂掉后导致的死锁;方案2最大的优势在于性能超强;在实际生产过程中,结合自身情况来决定最适合的分布式锁方案是架构师的必修课。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值