一、锁的作用   

  当多线程执行某一业务时(特别是对数据的更新、新增)等操作,可能就会出现多个线程对同一条数据进行修改。其最终的结果一
定与你期望的结果“不太一样”,这就与需要一把锁来控制线程排排队了 - java内部为我们提供了解决方案,可以使用synchronized
或Lock等方式来实现。
  但是在生产过程中,因为性能的关系,多数公司都会采用多台服务器来搭建”分布式”。一条请求过来之后,不一定会打到哪台服务
器上,这就保证不了多台服务器的某一”关键业务”同一时间只会有一条线程进行执行。这时就需要一个“媒介”来充当"锁"这个角色,这
个“媒介”需要满足一些特性才能胜任这个工作:
(1)各个服务器都可以对其进行获取和操作(基于数据库的实现方式);
(2)高性能的获取和释放锁;
(3)具备非阻塞锁特性,如果获取不到锁则立即返回失败;
(4)具备锁等待的特性,即没有获取到锁将继续等待获取锁;
(5)具有锁失效机制,防止死锁;
(6)具备可重入特性;

二、分布式锁的一种实现   

  高性能、失效时间、可重入、非阻塞等特性,Redis都能满足,所以基于Redis实现是解决分布式锁的一种方式。基于Redis的分布式
锁的实现思想:
  A、加锁
    1、计算开始时间,防止一直获取不到锁而死循环;
    2、根据系统名称:业务名称:主键ID来创建一个分布式锁的key;
    3、循环获取分布式锁(尝试时间=当前时间-开始时间<5000毫秒);
    4、使用setnx方法获取分布式锁(key,value - 当前系统时间+"$TRUE",失效时间 - 10毫秒);
    5、如果设置成功则获取分布式锁成功,返回true;
    6、如果获取失败则睡5毫秒,继续尝试,直到过了时间;
    7、如果时间之内没有获取到分布式所,则返回失败;
  B、解锁
    1、根据系统号,业务名称,主键拼接分布式锁的key;
    2、根据key获取到真实的key(使用keys方法);
    3、判断真实的key是否为存在,是否唯一,如果否则为空;
    4、如果key唯一则进行删除;
  栗子:

/**
* Jedis的工具类.
*/
public class JedisUtil {
private static final Logger LOG = LoggerFactory.getLogger(JedisUtil.class);
private static Map<String, JedisUtil> uniqueInstance = new HashMap();
private JedisPool jedisPool;
private static JedisUtil jedisUtil = null; public JedisUtil() {
super();
} /**
* 选择需要链接哪个库
* @param serverName
*/
public JedisUtil(String serverName) {
if (StringUtils.isBlank(serverName)) {
throw new RuntimeException("请指定Redis主或从库!");
} else {
//实际使用需从配置文件中获取
String ip = "127.0.0.1";
Integer port = 6379;
String pwd = "test";
//仅仅是测试用
String redisPoolMaxActive = "30";
if ("master".equals(serverName)) {
//链接到主库
this.initialPool(ip, port, pwd, redisPoolMaxActive);
}else{
//可以链接为别的Redis库
}
}
} /**
* 初始化jedisPool
* @param ip
* @param port
* @param pwd
* @param redisPoolMaxActive
*/
private void initialPool(String ip, int port, String pwd, String redisPoolMaxActive) {
JedisPoolConfig config = new JedisPoolConfig();
//最大连接数,默认8个
config.setMaxTotal(256);
//最大空闲连接数,默认8个
config.setMaxIdle(256);
if (StringUtils.isNotBlank(redisPoolMaxActive)) {
Integer redisPoolMaxActiveInt = Integer.valueOf(Integer.parseInt(redisPoolMaxActive));
if (redisPoolMaxActiveInt.intValue() > 512) {
redisPoolMaxActiveInt = Integer.valueOf(512);
} config.setMaxTotal(redisPoolMaxActiveInt.intValue());
config.setMaxIdle(redisPoolMaxActiveInt.intValue());
}
//获取连接时的最大等待毫秒数
config.setMaxWaitMillis(1000L);
//在获取连接的时候检查有效性,默认false
config.setTestOnBorrow(true);
config.setTestOnReturn(true);
//在空闲时检查有效性,默认false
config.setTestWhileIdle(true);
//每次逐出检查时 逐出的最大数目。如果为负数就是: 1/abs(n), 默认3
config.setNumTestsPerEvictionRun(-1);
//逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1
config.setTimeBetweenEvictionRunsMillis(30000L);
//逐出连接的最小空闲时间,默认1800000毫秒(30分钟)
config.setMinEvictableIdleTimeMillis(720000L);
//对象空闲多久后逐出,当空闲时间>该值且空闲连接 > 最大空闲数时直接逐出,不再根据MinEvictableIdleTimeMillis判断 (默认逐出策略)
config.setSoftMinEvictableIdleTimeMillis(360000L);
this.jedisPool = new JedisPool(config, ip, port, 2000, pwd);
LogUtils.info(LOG, "jedisPool init finish", new Object[]{"jedisPool", this.jedisPool});
} /**
* 单例获取jedisPool
* @param serverName Redis库的名称
* @return
*/
public static JedisUtil getInstance(String serverName) {
jedisUtil = (JedisUtil)uniqueInstance.get(serverName);
if(jedisUtil == null) {
Class var1 = JedisUtil.class;
synchronized(JedisUtil.class) {
jedisUtil = (JedisUtil)uniqueInstance.get(serverName);
if(jedisUtil == null) {
jedisUtil = new JedisUtil(serverName);
uniqueInstance.put(serverName, jedisUtil);
}
}
}
return jedisUtil;
} public Long setnx(String key, String value, int expireTime, int dbIndex) {
Jedis jedis = null; Long var1;
try {
jedis = this.jedisPool.getResource();
jedis.select(dbIndex);
Long ret = jedis.setnx(key, value);
if(ret.longValue() == 1L && expireTime > 0) {
jedis.expire(key, expireTime);
} var1 = ret;
} catch (Exception var11) {
LogUtils.error(LOG, "redis setnx error. ", var11, new Object[0]);
throw var11;
} finally {
if(jedis != null) {
jedis.close();
} } return var1;
} /**
* 查找key
* @param sKey
* @param index
* @return
*/
public Set<String> key(String sKey, int index) {
Jedis jedis = null; Set var2;
try {
jedis = this.jedisPool.getResource();
jedis.select(index);
var2 = jedis.keys(sKey);
} catch (Exception var8) {
LogUtils.error(LOG, "redis key error. ", var8, new Object[0]);
throw var8;
} finally {
if(jedis != null) {
jedis.close();
} } return var2;
} /**
* 删除key
* @param sKey
* @param index
* @return
*/
public Long del(String sKey, int index) {
Jedis jedis = null; Long var3;
try {
jedis = this.jedisPool.getResource();
jedis.select(index);
var3 = jedis.del(sKey);
} catch (Exception var8) {
LogUtils.error(LOG, "redis del error. ", var8, new Object[0]);
throw var8;
} finally {
if(jedis != null) {
jedis.close();
} } return var3;
}
}

分布式锁的工具类:

public class LockRedisService {
private static final Logger LOG = LoggerFactory.getLogger(LockRedisService.class);
private static final Integer EXPIRE_SECOND = Integer.valueOf(10);
public LockRedisService() {} /**
* 尝试获取分布式锁
* @param systemName 系统名称
* @param business 业务名称
* @param keyfix 主键
* @return
*/
public static Boolean tryLock(String systemName, String business, String keyfix) {
long start = System.currentTimeMillis();
String key = String.format("lock:%s:%s:%s", new Object[]{systemName, business, keyfix});
LogUtils.info(LOG, "尝试获取分布式锁", new Object[]{"key", key}); do {
try {
long ret = JedisUtil.getInstance("master").setnx(key, System.currentTimeMillis() + "$TRUE", EXPIRE_SECOND, 1).longValue();
if(ret == 1L) {
LogUtils.info(LOG, "成功获得分布式锁", new Object[]{"key", key});
return Boolean.TRUE;
} Thread.sleep(5L);
} catch (Exception var8) {
LogUtils.error(LOG, "获取锁失败", var8, new Object[0]);
}
} while(System.currentTimeMillis() - start < 5000L); LogUtils.warn(LOG, "获取分布式锁失败", new Object[]{"key", key});
return Boolean.FALSE;
} /**
* 解锁
* @param systemName 系统名称
* @param business 业务名称
* @param keyfix 主键
* @return
*/
public static Boolean unlock(String systemName, String business, String keyfix) {
String key = String.format("lock:%s:%s:%s", new Object[]{systemName, business, keyfix}); try {
//1为使用哪个Redis库,正常应该写为枚举类
Set<String> realKeySet = JedisUtil.getInstance("master").key(key,1);
if(CollectionUtils.isEmpty(realKeySet)) {
return null;
} else if(realKeySet.size() > 1) {
LogUtils.warn(LOG, "lock key 通配符存在多个", new Object[]{"key", key});
return null;
} else {
String realKey = (String)realKeySet.iterator().next();
JedisUtil.getInstance("master").del(realKey, 1);
}
} catch (Exception var5) {
LogUtils.error(LOG, "解锁失败", var5, new Object[0]);
} LogUtils.info(LOG, "解锁成功", new Object[]{"key", key});
return Boolean.valueOf(true);
}
}

测试类:

public class Test {
public static void main(String[] args) {
//获取cpu的核心数
int count = Runtime.getRuntime().availableProcessors();
//创建线程池
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(count, count * 10,
60L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(40960), new ThreadPoolExecutor.AbortPolicy());
//多线程执行业务
threadPool.execute(()->{
String bussiness = "测试分布式锁";
String key = "TestLock:" + 123;
//尝试获取分布式锁
if (LockRedisService.tryLock("测试系统", bussiness, key)) {
System.out.println("---获取分布式锁成功---");
try {
//可能会抛出异常
System.out.println("执行逻辑");
}catch (Exception e){
System.out.println("执行逻辑错误: "+e);
}finally {
//执行完毕后解锁
LockRedisService.unlock("测试系统", bussiness, key);
}
}else {
System.out.println("获取分布式锁失败");
}
});
}
}

Redis的分布式锁的更多相关文章

  1. 基于redis 实现分布式锁的方案

    在电商项目中,经常有秒杀这样的活动促销,在并发访问下,很容易出现上述问题.如果在库存操作上,加锁就可以避免库存卖超的问题.分布式锁使分布式系统之间同步访问共享资源的一种方式 基于redis实现分布式锁 ...

  2. 用Redis构建分布式锁-RedLock(真分布)

    在不同进程需要互斥地访问共享资源时,分布式锁是一种非常有用的技术手段. 有很多三方库和文章描述如何用Redis实现一个分布式锁管理器,但是这些库实现的方式差别很大,而且很多简单的实现其实只需采用稍微增 ...

  3. 用Redis实现分布式锁 与 实现任务队列(转)

    这一次总结和分享用Redis实现分布式锁 与 实现任务队列 这两大强大的功能.先扯点个人观点,之前我看了一篇博文说博客园的文章大部分都是分享代码,博文里强调说分享思路比分享代码更重要(貌似大概是这个意 ...

  4. 利用多写Redis实现分布式锁原理与实现分析(转)

    利用多写Redis实现分布式锁原理与实现分析   一.关于分布式锁 关于分布式锁,可能绝大部分人都会或多或少涉及到. 我举二个例子:场景一:从前端界面发起一笔支付请求,如果前端没有做防重处理,那么可能 ...

  5. Redis实现分布式锁

    http://redis.io/topics/distlock 在不同进程需要互斥地访问共享资源时,分布式锁是一种非常有用的技术手段. 有很多三方库和文章描述如何用Redis实现一个分布式锁管理器,但 ...

  6. 基于redis的分布式锁

    <?php /** * 基于redis的分布式锁 * * 参考开源代码: * http://nleach.com/post/31299575840/redis-mutex-in-php * * ...

  7. Redis实现分布式锁与任务队列

    Redis实现分布式锁 与 实现任务队列 这一次总结和分享用Redis实现分布式锁 与 实现任务队列 这两大强大的功能.先扯点个人观点,之前我看了一篇博文说博客园的文章大部分都是分享代码,博文里强调说 ...

  8. 使用Redis实现分布式锁

    在天猫.京东.苏宁等等电商网站上有很多秒杀活动,例如在某一个时刻抢购一个原价1999现在秒杀价只要999的手机时,会迎来一个用户请求的高峰期,可能会有几十万几百万的并发量,来抢这个手机,在高并发的情形 ...

  9. 基于Redis实现分布式锁(1)

    转自:http://blog.csdn.net/ugg/article/details/41894947 背景在很多互联网产品应用中,有些场景需要加锁处理,比如:秒杀,全局递增ID,楼层生成等等.大部 ...

  10. redis咋么实现分布式锁,redis分布式锁的实现方式,redis做分布式锁 积极正义的少年

    前言 分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介 ...

随机推荐

  1. LuNamp和LuManager的目录结构

    /|---- etc/|-------- my.cnf #mysql配置文件|-------- ipfw.conf #ipfw防火墙的配置文件(仅FreeBSD)|---- home/|------- ...

  2. POJ2115 C Looooops ——模线性方程(扩展gcd)

    题目链接:http://poj.org/problem?id=2115 C Looooops Time Limit: 1000MS   Memory Limit: 65536K Total Submi ...

  3. 专网IP和公网IP的区别是什么

    专网ip是自己网内用,公网的话就全球有效 最大区别是公网IP世界只有一个,私网IP可以重复,但是在一个局域网内不能重复 访问互联网是需要IP地址的,IP地址又分为公网IP和私网IP,访问互联网需要公网 ...

  4. Android高手应该精通哪些内容

    很多Android开发者已经度过了初级.中级,如何成为一个Android高手呢? Android123就各个级别的程序员应该掌握哪些内容作为下面分类. 一.初级 1. 拥有娴熟的Java基础,理解设计 ...

  5. 从Github上下载了项目,导入Android Studio,gradle 报错,应该怎么修改

    一.从Github上获取源代码 我这里是直接下载ZIP文件 二.在本机的Android Studio上新建一个空白项目,目的主要是与刚从Github上下载的项目文件结构做对比 三.替换gradle文件 ...

  6. 精选Java面试题(二)

    Java中的方法覆盖重写(Overriding)和方法重载(Overloading)是什么意思? Java中的方法重载发生在同一个类里面两个或者是多个方法的方法名相同但是参数不同的情况.与此相对,方法 ...

  7. certificate unknown(46) - 中间证书问题排查

    因为腾讯云的网站备案迟迟没有批下来,因此使用了朋友在阿里云的域名yk,我则申请了一台阿里云服务器,并将域名解析映射至该服务器.SSL证书则是在腾讯云上申请的,使用了Apache文件夹中的文件,放置在c ...

  8. CentOS环境 升级Python2.6.6至2.7.5

      1.查看当前Python版本 # python -V Python 2.6.6 # python -V Python 2.6.6 2.下载Python2.7.5源码 # wget http://p ...

  9. 从使用os.system)在python命令(重定向标准输入输出

    从使用os.system)在python命令(重定向标准输入输出 python 标准输出stdout stdio os.system通常我可以通过改变sys.stdout的值在python更改标准输出 ...

  10. 3 pyspark学习---sparkContext概述

    1 Tutorial Spark本身是由scala语言编写,为了支持py对spark的支持呢就出现了pyspark.它依然可以通过导入Py4j进行RDDS等操作. 2 sparkContext (1) ...