redis需单点部署(非集群)
redis分布式锁解决多个应用进程间同步操作整理了很多网上文档 发现都没有解决如下问题。。。
参考
...
1.时间同步问题
2.在一个进程cash后失效时间后自动释放锁
3.有些多线程race condition没有考虑到
以下java版本实现彻底解决 充分测试
** 方案一(推荐)**
import java.util.List;import java.util.UUID;import redis.clients.jedis.Jedis;import redis.clients.jedis.JedisPool;import redis.clients.jedis.Transaction;import redis.clients.jedis.exceptions.JedisException;/** * Jedis实现分布式锁 * * @author 三文鱼 * */public class DistributionLock { private final JedisPool jedisPool; public DistributionLock(JedisPool jedisPool) { this.jedisPool = jedisPool; } /** * 获取分布式锁 * * @param lockName * 竞争获取锁key * @param acquireTimeoutInMS * 获取锁超时时间 * @param lockTimeoutInMS * 锁的超时时间 * @return 获取锁标识 */ public String acquireLockWithTimeout(String lockName, long acquireTimeoutInMS, long lockTimeoutInMS) { Jedis conn = null; boolean broken = false; String retIdentifier = null; try { conn = jedisPool.getResource(); String identifier = UUID.randomUUID().toString(); String lockKey = "lock:" + lockName; int lockExpire = (int) (lockTimeoutInMS / 1000); long end = System.currentTimeMillis() + acquireTimeoutInMS; while (System.currentTimeMillis() < end) { if (conn.setnx(lockKey, identifier) == 1) { conn.expire(lockKey, lockExpire); retIdentifier = identifier; } if (conn.ttl(lockKey) == -1) { conn.expire(lockKey, lockExpire); } try { Thread.sleep(10); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); } } } catch (JedisException je) { if (conn != null) { broken = true; jedisPool.returnBrokenResource(conn); } } finally { if (conn != null && !broken) { jedisPool.returnResource(conn); } } return retIdentifier; } /** * 释放锁 * @param lockName 竞争获取锁key * @param identifier 释放锁标识 * @return */ public boolean releaseLock(String lockName, String identifier) { Jedis conn = null; boolean broken = false; String lockKey = "lock:" + lockName; boolean retFlag = false; try { conn = jedisPool.getResource(); while (true) { conn.watch(lockKey); if (identifier.equals(conn.get(lockKey))) { Transaction trans = conn.multi(); trans.del(lockKey); List
**
方案二(不完善,比如unlock没有做所有者校验等,如果误删其他线程持有的锁也会造成问题)**
/** * @author http://blog.csdn.net/java2000_wl * @version 1.0.0 */ public class RedisBillLockHandler implements IBatchBillLockHandler { private static final Logger LOGGER = LoggerFactory.getLogger(RedisBillLockHandler.class); private static final int DEFAULT_SINGLE_EXPIRE_TIME = 3; private static final int DEFAULT_BATCH_EXPIRE_TIME = 6; private final JedisPool jedisPool; /** * 构造 * @author http://blog.csdn.net/java2000_wl */ public RedisBillLockHandler(JedisPool jedisPool) { this.jedisPool = jedisPool; } /** * 获取锁 如果锁可用 立即返回true, 否则返回false * @author http://blog.csdn.net/java2000_wl * @param billIdentify * @return */ public boolean tryLock(IBillIdentify billIdentify) { return tryLock(billIdentify, 0L, null); } /** * 锁在给定的等待时间内空闲,则获取锁成功 返回true, 否则返回false * @author http://blog.csdn.net/java2000_wl * @param billIdentify * @param timeout * @param unit * @return */ public boolean tryLock(IBillIdentify billIdentify, long timeout, TimeUnit unit) { String key = (String) billIdentify.uniqueIdentify(); Jedis jedis = null; try { jedis = getResource(); long nano = System.nanoTime(); do { LOGGER.debug("try lock key: " + key); Long i = jedis.setnx(key, key); if (i == 1) { jedis.expire(key, DEFAULT_SINGLE_EXPIRE_TIME); LOGGER.debug("get lock, key: " + key + " , expire in " + DEFAULT_SINGLE_EXPIRE_TIME + " seconds."); return Boolean.TRUE; } else { // 存在锁 if (LOGGER.isDebugEnabled()) { String desc = jedis.get(key); LOGGER.debug("key: " + key + " locked by another business:" + desc); } } if (timeout == 0) { break; } Thread.sleep(300); } while ((System.nanoTime() - nano) < unit.toNanos(timeout)); return Boolean.FALSE; } catch (JedisConnectionException je) { LOGGER.error(je.getMessage(), je); returnBrokenResource(jedis); } catch (Exception e) { LOGGER.error(e.getMessage(), e); } finally { returnResource(jedis); } return Boolean.FALSE; } /** * 如果锁空闲立即返回 获取失败 一直等待 * @author http://blog.csdn.net/java2000_wl * @param billIdentify */ public void lock(IBillIdentify billIdentify) { String key = (String) billIdentify.uniqueIdentify(); Jedis jedis = null; try { jedis = getResource(); do { LOGGER.debug("lock key: " + key); Long i = jedis.setnx(key, key); if (i == 1) { jedis.expire(key, DEFAULT_SINGLE_EXPIRE_TIME); LOGGER.debug("get lock, key: " + key + " , expire in " + DEFAULT_SINGLE_EXPIRE_TIME + " seconds."); return; } else { if (LOGGER.isDebugEnabled()) { String desc = jedis.get(key); LOGGER.debug("key: " + key + " locked by another business:" + desc); } } Thread.sleep(300); } while (true); } catch (JedisConnectionException je) { LOGGER.error(je.getMessage(), je); returnBrokenResource(jedis); } catch (Exception e) { LOGGER.error(e.getMessage(), e); } finally { returnResource(jedis); } } /** * 释放锁 * @author http://blog.csdn.net/java2000_wl * @param billIdentify */ public void unLock(IBillIdentify billIdentify) { Listlist = new ArrayList (); list.add(billIdentify); unLock(list); } /** * 批量获取锁 如果全部获取 立即返回true, 部分获取失败 返回false * @author http://blog.csdn.net/java2000_wl * @date 2013-7-22 下午10:27:44 * @param billIdentifyList * @return */ public boolean tryLock(List billIdentifyList) { return tryLock(billIdentifyList, 0L, null); } /** * 锁在给定的等待时间内空闲,则获取锁成功 返回true, 否则返回false * @author http://blog.csdn.net/java2000_wl * @param billIdentifyList * @param timeout * @param unit * @return */ public boolean tryLock(List billIdentifyList, long timeout, TimeUnit unit) { Jedis jedis = null; try { List needLocking = new CopyOnWriteArrayList (); List locked = new CopyOnWriteArrayList (); jedis = getResource(); long nano = System.nanoTime(); do { // 构建pipeline,批量提交 Pipeline pipeline = jedis.pipelined(); for (IBillIdentify identify : billIdentifyList) { String key = (String) identify.uniqueIdentify(); needLocking.add(key); pipeline.setnx(key, key); } LOGGER.debug("try lock keys: " + needLocking); // 提交redis执行计数 List