2017-07-12 10:38:44.0|分类: redis|浏览量: 3931
分布式锁应用场景比较多,百度一下很多,本文就略过了,直接描述实现过程 举个列子:电商搞秒杀活动,需要进行设置剩余数量,count必须是线程安全的!count暂时代表秒杀手机剩余数量! 步骤1:线程A获取redis分布式锁,如果获取锁成功,继续执行步骤2, 步骤2:线程A执行业务操作,设置count值为100 步骤3:线程A释放掉redis的分布式锁 步骤4:线程B获取redis的分布式锁,发现锁没有被使用,线程B则获取锁成功 步骤5:线程B设置count值为99 步骤6:线程B释放掉redis的分布式锁 上面流程有如下问题: (1)线程A成功获取锁后,突然线程A挂掉了,redis的分布式锁没有被释放,其他线程获取锁的时候发现锁被占用,锁不能获取成功。整个程序就挂掉了! 解决办法:给锁增加一个超时时间1s。超过1s后redis自动释放这个锁 (2)如果线程A执行业务操作,耗时2s,2s大于redis超时时间1s。在这期间redis自动释放锁,线程B成功获取了锁,并且将count设置成99。线程A执行完业务操作,设置count=100,这就出现了脏数据 解决办法:线程加锁的时候设置一个随机数,释放锁的时候进行比较,这把锁是否是自己加的锁 基于redis实现分布式锁,重点解决如下问题: (1)分布式锁必须要设置一个过期时间 (2)加锁和设置失效时间必须是原子操作 (3)线程加锁时候设置一个随机字符串 (4)释放锁其实包含get、判断、del三步操作,三步操作必须是原子性。 java实现基于redis实现分布式锁思想: (1)redis定义一个key (2)java获取锁 (3)java释放锁 代码实现: public interface DistributedLock { public boolean getLock();
public void releaseLock();
} public class RedisDistributedLock implements DistributedLock { private static final Logger logger = LoggerFactory.getLogger(RedisDistributedLock.class); public static final String LOCK_NODE ="LOCK"; ThreadLocal<String> threadLocal = new ThreadLocal<String>(); @Override public boolean getLock() { Jedis jedis = new Jedis("localhost"); String value = UUID.randomUUID().toString(); String ret = jedis.set(LOCK_NODE, value, "NX", "PX", 10000); logger.warn("ret="+ret); System.out.println("ret="+ret); if(ret!=null && ret.equals("OK")){ threadLocal.set(value); jedis.close(); return true; }else{ jedis.close(); } return false; } @Override public void releaseLock() { String script = readFileByLines("E:\\workhome\\workspaceMarsMvn\\redis\\src\\main\\resources\\unlock.lua"); System.err.println(script); Jedis jedis = new Jedis("localhost"); List<String> keys = new ArrayList<String>(); keys.add(LOCK_NODE); List<String> args = new ArrayList<String>(); args.add(threadLocal.get()); System.err.println("releaseLock" + keys.toString()+" | "+args.toString()); Object object = jedis.eval(script, keys, args); System.err.println("releaseLock" + object); jedis.close(); }
/** * 以行为单位读取文件,常用于读面向行的格式化文件 */ public static String readFileByLines(String fileName) { File file = new File(fileName); BufferedReader reader = null; StringBuffer content = new StringBuffer(); try { reader = new BufferedReader(new FileReader(file)); String tempString = null; // 一次读入一行,直到读入null为文件结束 while ((tempString = reader.readLine()) != null) { content.append(tempString+"\n"); } reader.close(); } catch (IOException e) { e.printStackTrace(); } finally { if (reader != null) { try { reader.close(); } catch (IOException e1) { } } } return content.toString(); } } unlock.lua代码 if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end |