两种分布式锁实现方案(二)
来源:互联网 发布:transe的算法思想 编辑:程序博客网 时间:2024/06/15 05:32
四。方案2,基于redis的分布式锁
/** * 分布式锁工厂类 */public class RedisLockUtil { private static final Logger logger = Logger.getLogger(RedisLockUtil.class); private static Object schemeLock = new Object(); private static Map<String, RedisLockUtil> instances = new ConcurrentHashMap(); public static RedisLockUtil getInstance(String schema) { RedisLockUtil u = instances.get(schema); if (u == null) { synchronized (schemeLock) { u = instances.get(schema); if (u == null) { LockObserver lo = new LockObserver(schema); u = new RedisLockUtil(schema, lo); instances.put(schema, u); } } } return u; } private Object mutexLock = new Object(); private Map<String, Object> mutexLockMap = new ConcurrentHashMap(); private Map<String, RedisReentrantLock> cache = new ConcurrentHashMap<String, RedisReentrantLock>(); private DelayQueue<RedisReentrantLock> dq = new DelayQueue<RedisReentrantLock>(); private AbstractLockObserver lo; public RedisLockUtil(String schema, AbstractLockObserver lo) { Thread th = new Thread(lo); th.setDaemon(false); th.setName("Lock Observer:"schema); th.start(); clearUselessLocks(schema); this.lo = lo; } public void clearUselessLocks(String schema) { Thread th = new Thread(new Runnable() { @Override public void run() { while (!SystemExitListener.isOver()) { try { RedisReentrantLock t = dq.take(); if (t.clear()) { String key = t.getKey(); synchronized (getMutex(key)) { cache.remove(key); } } t.resetCleartime(); } catch (InterruptedException e) { } } } }); th.setDaemon(true); th.setName("Lock cleaner:"schema); th.start(); } private Object getMutex(String key) { Object mx = mutexLockMap.get(key); if (mx == null) { synchronized (mutexLock) { mx = mutexLockMap.get(key); if (mx == null) { mx = new Object(); mutexLockMap.put(key, mx); } } } return mx; } private RedisReentrantLock getLock(String key, boolean addref) { RedisReentrantLock lock = cache.get(key); if (lock == null) { synchronized (getMutex(key)) { lock = cache.get(key); if (lock == null) { lock = new RedisReentrantLock(key, lo); cache.put(key, lock); } } } if (addref) { if (!lock.incRef()) { synchronized (getMutex(key)) { lock = cache.get(key); if (!lock.incRef()) { lock = new RedisReentrantLock(key, lo); cache.put(key, lock); } } } } return lock; } public void reset() { for (String s : cache.keySet()) { getLock(s, false).unlock(); } } /** * 尝试加锁 * 如果当前线程已经拥有该锁的话,直接返回,表示不用再次加锁,此时不应该再调用unlock进行解锁 * * @param key * @return * @throws Exception * @throws InterruptedException * @throws KeeperException */ public LockStat lock(String key) { return lock(key, -1); } public LockStat lock(String key, int timeout) { RedisReentrantLock ll = getLock(key, true); ll.incRef(); try { if (ll.isOwner(false)) { ll.descrRef(); return LockStat.NONEED; } if (ll.lock(timeout)) { return LockStat.SUCCESS; } else { ll.descrRef(); if (ll.setCleartime()) { dq.put(ll); } return null; } } catch (LockNotExistsException e) { ll.descrRef(); return lock(key, timeout); } catch (RuntimeException e) { ll.descrRef(); throw e; } } public void unlock(String key, LockStat stat) { unlock(key, stat, false); } public void unlock(String key, LockStat stat, boolean keepalive) { if (stat == null) return; if (LockStat.SUCCESS.equals(stat)) { RedisReentrantLock lock = getLock(key, false); boolean candestroy = lock.unlock(); if (candestroy && !keepalive) { if (lock.setCleartime()) { dq.put(lock); } } } } public static enum LockStat { NONEED, SUCCESS }}
/** * 分布式锁本地代理类 */public class RedisReentrantLock implements Delayed { private static final Logger logger = Logger.getLogger(RedisReentrantLock.class); private ReentrantLock reentrantLock = new ReentrantLock(); private RedisLock redisLock; private long timeout = 3 * 60; private CountDownLatch lockcount = new CountDownLatch(1); private String key; private AbstractLockObserver observer; private int ref = 0; private Object refLock = new Object(); private boolean destroyed = false; private long cleartime = -1; public RedisReentrantLock(String key, AbstractLockObserver observer) { this.key = key; this.observer = observer; initWriteLock(); } public boolean isDestroyed() { return destroyed; } private synchronized void initWriteLock() { redisLock = new RedisLock(key, new LockListener() { @Override public void lockAcquired() { lockcount.countDown(); } @Override public long getExpire() { return 0; } @Override public void lockError() { /*synchronized(mutex){mutex.notify();}*/ lockcount.countDown(); } }, observer); } public boolean incRef() { synchronized (refLock) { if (destroyed) return false; ref; } return true; } public void descrRef() { synchronized (refLock) { ref--; } } public boolean clear() { if (destroyed) return true; synchronized (refLock) { if (ref > 0) { return false; } destroyed = true; redisLock.clear(); redisLock = null; return true; } } public boolean lock(long timeout) throws LockNotExistsException { if (timeout <= 0) timeout = this.timeout; //incRef(); reentrantLock.lock();//多线程竞争时,先拿到第一层锁 if (redisLock == null) { reentrantLock.unlock(); //descrRef(); throw new LockNotExistsException(); } try { lockcount = new CountDownLatch(1); boolean res = redisLock.trylock(timeout); if (!res) { lockcount.await(timeout, TimeUnit.SECONDS); //mutex.wait(timeout*1000); if (!redisLock.doExpire()) { reentrantLock.unlock(); return false; } } return true; } catch (InterruptedException e) { reentrantLock.unlock(); return false; } } public boolean lock() throws LockNotExistsException { return lock(timeout); } public boolean unlock() { if (!isOwner(true)) { try { throw new RuntimeException("big ================================================ error.key:"key); } catch (Exception e) { logger.error("err:"e, e); } return false; } try { redisLock.unlock(); reentrantLock.unlock();//多线程竞争时,释放最外层锁 } catch (RuntimeException e) { reentrantLock.unlock();//多线程竞争时,释放最外层锁 throw e; } finally { descrRef(); } return canDestroy(); } public boolean canDestroy() { synchronized (refLock) { return ref <= 0; } } <a href="http://www.nbso.ca/"> nbso online casino reviews </a> public String getKey() { return key; } public void setKey(String key) { this.key = key; } public boolean isOwner(boolean check) { synchronized (refLock) { if (redisLock == null) { logger.error("reidsLock is null:key="key); return false; } boolean a = reentrantLock.isHeldByCurrentThread(); boolean b = redisLock.isOwner(); if (check) { if (!a || !b) { logger.error(key";a:"a";b:"b); } } return a && b; } } public boolean setCleartime() { synchronized (this) { if (cleartime > 0) return false; this.cleartime = System.currentTimeMillis() 10 * 1000; return true; } } public void resetCleartime() { synchronized (this) { this.cleartime = -1; } } @Override public int compareTo(Delayed object) { if (object instanceof RedisReentrantLock) { RedisReentrantLock t = (RedisReentrantLock) object; long l = this.cleartime - t.cleartime; if (l > 0) return 1; //比当前的小则返回1,比当前的大则返回-1,否则为0 else if (l < 0) return -1; else return 0; } return 0; } @Override public long getDelay(TimeUnit unit) { long d = unit.convert(cleartime - System.currentTimeMillis(), TimeUnit.MILLISECONDS); return d; }}
/** * 使用Redis实现的分布式锁 * 基本工作原理如下: * 1. 使用setnx(key,时间戮 超时),如果设置成功,则直接拿到锁 * 2. 如果设置不成功,获取key的值v1(它的到期时间戮),跟当前时间对比,看是否已经超时 * 3. 如果超时(说明拿到锁的结点已经挂掉),v2=getset(key,时间戮 超时 1),判断v2是否等于v1,如果相等,加锁成功,否则加锁失败,等过段时间再重试(200MS) */public class RedisLock implements LockListener { private String key; private boolean owner = false; private AbstractLockObserver observer = null; private LockListener lockListener = null; private boolean waiting = false; private long expire;//锁超时时间,以秒为单位 private boolean expired = false; public RedisLock(String key, LockListener lockListener, AbstractLockObserver observer) { this.key = key; this.lockListener = lockListener; this.observer = observer; } public boolean trylock(long expire) { synchronized (this) { if (owner) { return true; } this.expire = expire; this.expired = false; if (!waiting) { owner = observer.tryLock(key, expire); if (!owner) { waiting = true; observer.addLockListener(key, this); } } return owner; } } public boolean isOwner() { return owner; } public void unlock() { synchronized (this) { observer.unLock(key); owner = false; } } public void clear() { synchronized (this) { if (waiting) { observer.removeLockListener(key); waiting = false; } } } public boolean doExpire() { synchronized (this) { if (owner) return true; if (expired) return false; expired = true; clear(); } return false; } @Override public void lockAcquired() { synchronized (this) { if (expired) { unlock(); return; } owner = true; waiting = false; } lockListener.lockAcquired(); } @Override public long getExpire() { return this.expire; } @Override public void lockError() { synchronized (this) { owner = false; waiting = false; lockListener.lockError(); } }}
public class LockObserver extends AbstractLockObserver implements Runnable { private CacheRedisClient client; private Object mutex = new Object(); private Map<String, LockListener> lockMap = new ConcurrentHashMap(); private boolean stoped = false; private long interval = 500; private boolean terminated = false; private CountDownLatch doneSignal = new CountDownLatch(1); public LockObserver(String schema) { client = new CacheRedisClient(schema); SystemExitListener.addTerminateListener(new ExitHandler() { public void run() { stoped = true; try { doneSignal.await(); } catch (InterruptedException e) { } } }); } public void addLockListener(String key, LockListener listener) { if (terminated) { listener.lockError(); return; } synchronized (mutex) { lockMap.put(key, listener); } } public void removeLockListener(String key) { synchronized (mutex) { lockMap.remove(key); } } @Override public void run() { while (!terminated) { long p1 = System.currentTimeMillis(); Map<String, LockListener> clone = new HashMap(); synchronized (mutex) { clone.putAll(lockMap); } Set<String> keyset = clone.keySet(); if (keyset.size() > 0) { ConnectionFactory.setSingleConnectionPerThread(keyset.size()); for (String key : keyset) { LockListener ll = clone.get(key); try { if (tryLock(key, ll.getExpire())) { ll.lockAcquired(); removeLockListener(key); } } catch (Exception e) { ll.lockError(); removeLockListener(key); } } ConnectionFactory.releaseThreadConnection(); } else { if (stoped) { terminated = true; doneSignal.countDown(); return; } } try { long p2 = System.currentTimeMillis(); long cost = p2 - p1; if (cost <= interval) { Thread.sleep(interval - cost); } else { Thread.sleep(interval * 2); } } catch (InterruptedException e) { } } } /** * 超时时间单位为s!!! * * @param key * @param expire * @return */ public boolean tryLock(final String key, final long expireInSecond) { if (terminated) return false; final long tt = System.currentTimeMillis(); final long expire = expireInSecond * 1000; final Long ne = tt expire; List<Object> mm = client.multi(key, new MultiBlock() { @Override public void execute() { transaction.setnxObject(key, ne); transaction.get(SafeEncoder.encode(key)); } }); Long res = (Long) mm.get(0); if (new Long(1).equals(res)) { return true; } else { byte[] bb = (byte[]) mm.get(1); Long ex = client.deserialize(bb); if (ex == null || tt > ex) { Long old = client.getSet(key, new Long(ne 1)); if (old == null || (ex == null && old == null) || (ex != null && ex.equals(old))) { return true; } } } return false; } public void unLock(String key) { client.del(key); }}
使用本方案实现的分布式锁,可以完美地解决锁重入问题;通过引入超时也避免了死锁问题;性能方面,笔者自测试结果如下:
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最大的优势在于性能超强;在实际生产过程中,结合自身情况来决定最适合的分布式锁方案是架构师的必修课。
1 0
- 两种分布式锁实现方案(二)
- 两种分布式锁实现方案二(14)
- 两种分布式锁实现方案
- 两种分布式锁实现方案(一)
- 两种分布式锁实现方案
- 两种分布式锁实现方案(一)
- 两种分布式锁实现方案一(13)
- 分布式锁的两种实现方式
- PHP:分布式事务及两阶段提交方案实现思路
- 一个分布式rpc框架的实现方案(二)
- ActiveMQ解决分布式事务方案以及代码实现(二)
- 分布式锁的三种实现方式 / 分布式锁原理及实现方式 / 分布式锁1 Java常用技术方案
- 基于redis 实现分布式锁的方案
- 一个简单的分布式锁实现方案
- 实现Apache虚拟主机的两种方案
- Javascript Callback的两种实现方案
- bootstrap 下拉菜单两种实现方案
- 离散傅里叶变换的两种实现方案
- JSP脚本标识
- spring整合springmvc和mybatis
- 滤波电容、去耦电容、旁路电容
- 在Emacs中使用git
- Nutch核心流程
- 两种分布式锁实现方案(二)
- 使用weinre远程调试手机网页(nodejs、phoneGap)
- 奇怪的Java题:为什么1000 == 1000返回为False,而100 == 100会返回为True?
- 关于Androidstudio版本升级导致项目打开停留在gradle构建解决
- google hacking
- HDU 3987 Harry Potter and the Forbidden Forest
- java基础
- Git branch
- Networx蓝屏问题