使用redis来实现分布式锁
在实际的工作中,有部分的特定场景需要使用到分布式锁来进行跨服务器资源的统一调配。之前在一家医疗互联网公司,因为黄牛抢号等原因,造成同一个患者同一时段在同一个医生处,挂到了两个及以上的号,我对之前我司实现的代码进行了封装和改进,在github上提供了源码,有需要的朋友,可以下载代码,并用maven打包成jar包,使用起来比较方便。
源码地址:https://github.com/mantuliu/distributed
核心feature:
redis的setnx()方法,此方法提供了一个原子操作,可以保证有且只有一个分布式的调用返回值为1,在分布式锁的概念里,则代表此锁被此次调用的线程占用;
redis的expire()方法,通过此方法来设置此锁的过期时间;
对于死锁的情况,封装的分布式锁包可以自动解锁;
每次获取锁的线程,都会被标识,当此线程还没有释放锁时,此线程继续调用trylock方法,还可以获得该锁,只有获取该锁的线程才有unlock()释放锁的权利;
支持trylock()和trylock(long timeout, TimeUnit unit)两种方式;
使用应用服务器提供的jedis实例;
对于当前服务器试图获取该锁的线程数量进行监控,当数量大于阀值时,后续线程在trylock(long timeout, TimeUnit unit)及trylock()时,直接返回失败,阀值可以设置;
锁的过期时间默认值是5秒,可以根据实际情况进行设置;
锁的键值前缀默认值是mantu:dislock:,可以根据实际情况进行设置;
通过LockSupport类来获取锁,使得试图获取同一把锁的线程得到的对象是同一个。
源码解析:
CommonType类
public class CommonType { public static int WAITLOCKERS = 2;//当前服务器等待锁的线程数量,如果超过或等于此值,当前线程直接返回,不再等待锁
public static String REDISKEY="mantu:dislock:";//redis下key前缀
public static int LOCKEXPIRETIME = 5;//锁的过期时间,单位秒,默认5秒过期
}
DisLock接口
public interface DisLock{ boolean tryLock(Jedis jedis);
boolean tryLock(long time, TimeUnit unit,Jedis jedis) throws InterruptedException;
void unlock(Jedis jedis);
}
RedisDisLock类,实际的管理锁的类
public class RedisDisLock implements DisLock{
private static final Logger LOG = LoggerFactory.getLogger(RedisDisLock.class);
private transient Thread exclusiveOwnerThread; String lockKey="";
AtomicInteger waitToLock=new AtomicInteger(0); public RedisDisLock(String lockKey){
this.lockKey=CommonType.REDISKEY+lockKey;
} public boolean tryLock(Jedis jedis) {
Thread thread = Thread.currentThread();
if(thread==this.getExclusiveOwnerThread()){
return true;
}
Long i = jedis.setnx(lockKey, System.currentTimeMillis()+"");
if(i.intValue()==1){
jedis.expire(lockKey, CommonType.LOCKEXPIRETIME);
setExclusiveOwnerThread(thread);
return true;
}
else{//对于可能性非常低的死锁情况进行解锁
String initTime = jedis.get(lockKey);
if(initTime==null){
LOG.debug("initTime's value is null");
return false;
}
long iniTime=0L;
try{
iniTime = Long.parseLong(initTime);
}
catch(NumberFormatException nfex){
LOG.warn(nfex.getMessage());
jedis.expire(lockKey, 1);
return false;
}
if(((System.currentTimeMillis()-iniTime)/1000-CommonType.LOCKEXPIRETIME-1)>0){
String oldTime = jedis.getSet(lockKey, System.currentTimeMillis()+"");//对于及其极端的情况,lock被线程1处理掉了,但是又被线程2getset新的值了,通过下一次调用trylock()方法处理
if(oldTime==null){
LOG.info("oldTime is null");
return false;
}
if(initTime.equals(oldTime)){
release(jedis);
}
}
}
return false;
} public boolean tryLock(long timeout, TimeUnit unit,Jedis jedis) throws InterruptedException {
long nanosTimeout = unit.toNanos(timeout);
long lastTime = System.nanoTime();
if(tryLock(jedis)){
return true;
}
try{
int waitLockers = waitToLock.getAndIncrement();
if(waitLockers>=CommonType.WAITLOCKERS){
LOG.debug("wait the lock' thread num is much,so return flase");
return false;
}
for(;;){
if(tryLock(jedis)){
return true;
}
if (nanosTimeout <= 0){
LOG.debug("getlock timeout");
return false;
}
if(nanosTimeout>100000){
LockSupport.parkNanos(100000);//中断100毫秒
}
long now = System.nanoTime();
nanosTimeout -= now - lastTime;
lastTime = now;
if (nanosTimeout <= 0){
LOG.debug("getlock timeout");
return false;
}
if (Thread.interrupted()){
throw new InterruptedException();
}
}
}
finally{
waitToLock.decrementAndGet();
}
} public void unlock(Jedis jedis) {
Thread thread = Thread.currentThread();
if(thread==this.getExclusiveOwnerThread()){
LOG.debug("unlock the thread {}",thread.getId());
release(jedis);
}
} private void release(Jedis jedis){
setExclusiveOwnerThread(null);
jedis.del(lockKey);
} /**
* Sets the thread that currently owns exclusive access. A
* <tt>null</tt> argument indicates that no thread owns access.
* This method does not otherwise impose any synchronization or
* <tt>volatile</tt> field accesses.
*/
protected final void setExclusiveOwnerThread(Thread t) {
exclusiveOwnerThread = t;
} /**
* Returns the thread last set by
* <tt>setExclusiveOwnerThread</tt>, or <tt>null</tt> if never
* set. This method does not otherwise impose any synchronization
* or <tt>volatile</tt> field accesses.
* @return the owner thread
*/
protected final Thread getExclusiveOwnerThread() {
return exclusiveOwnerThread;
}
}
LockSupport类,实际的业务代码首先要通过LockSupport来获取redis锁的对象,再使用
public class LockSupport { static ConcurrentHashMap <String,RedisDisLock>lockMap = new ConcurrentHashMap<String,RedisDisLock>(); public static DisLock getRedisLock(String lockKey){
RedisDisLock lock=null;
if(lockMap.contains(lockKey)){
lock = lockMap.get(lockKey);
}
else{
RedisDisLock lockN = new RedisDisLock(lockKey);
lock = lockMap.putIfAbsent(lockKey, lockN);
if(lock==null){
lock=lockN;
}
}
return lock;
}
}
RedisDisLockTest类是使用此jar的demo代码
public class RedisDisLockTest { public static void main(String [] args){
RedisDisLockTest test = new RedisDisLockTest();
//test.testOrder();
//test.testOrder2();
//test.testNOUnlock();
test.testOtherUnlock();
}
public void testOrder(){
JedisPool jp = new JedisPool("127.0.0.1",6379); for(int i=0;i<5;i++){
Jedis jedis = jp.getResource();
OrderThread th = new OrderThread("123456",jedis);
th.start();
}
}
public void testOrder2(){
JedisPool jp = new JedisPool("127.0.0.1",6379); for(int i=0;i<5;i++){
Jedis jedis = jp.getResource();
OrderThread th = new OrderThread("1234567",jedis);
th.start();
}
}
public void testNOUnlock(){
JedisPool jp = new JedisPool("127.0.0.1",6379); for(int i=0;i<5;i++){
Jedis jedis = jp.getResource();
TestNOUnlock th = new TestNOUnlock("12345678",jedis);
th.start();
}
}
public void testOtherUnlock(){
JedisPool jp = new JedisPool("127.0.0.1",6379); for(int i=0;i<5;i++){
Jedis jedis = jp.getResource();
TestOtherUnlock th = new TestOtherUnlock("unlock",jedis);
th.start();
}
}
class OrderThread extends Thread{
String lockKey="";
Jedis jedis;
public OrderThread(String lockKey,Jedis jedis){
this.lockKey=lockKey;
this.jedis=jedis;
}
public void run(){
DisLock lock = LockSupport.getRedisLock(lockKey);
try {
if(lock.tryLock(2,TimeUnit.SECONDS,jedis)){
System.out.println("订单"+lockKey+"创建成功!");
lock.unlock(jedis);
}
else{
System.out.println("没有成功获取到锁");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} class TestNOUnlock extends Thread{
String lockKey="";
Jedis jedis;
public TestNOUnlock(String lockKey,Jedis jedis){
this.lockKey=lockKey;
this.jedis=jedis;
}
public void run(){
DisLock lock = LockSupport.getRedisLock(lockKey);
try {
if(lock.tryLock(2,TimeUnit.SECONDS,jedis)){
System.out.println("订单"+lockKey+"创建成功!");
//lock.unlock(jedis);//no unlock
}
else{
System.out.println("没有成功获取到锁");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} class TestOtherUnlock extends Thread{
String lockKey="";
Jedis jedis;
public TestOtherUnlock(String lockKey,Jedis jedis){
this.lockKey=lockKey;
this.jedis=jedis;
}
public void run(){
DisLock lock = LockSupport.getRedisLock(lockKey);
if(lock.tryLock(jedis)){
System.out.println("订单"+lockKey+"创建成功!");
//lock.unlock(jedis);//no unlock
}
else{
lock.unlock(jedis);
System.out.println("TestOtherUnlock没有成功获取到锁");
} }
}
}
使用redis来实现分布式锁的更多相关文章
- 基于redis实现的分布式锁
基于redis实现的分布式锁 我们知道,在多线程环境中,锁是实现共享资源互斥访问的重要机制,以保证任何时刻只有一个线程在访问共享资源.锁的基本原理是:用一个状态值表示锁,对锁的占用和释放通过状态值来标 ...
- 一个Redis实现的分布式锁
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.redis.conne ...
- 基于Redis的简单分布式锁的原理
参考资料:https://redis.io/commands/setnx 加锁是为了解决多线程的资源共享问题.Java中,单机环境的锁可以用synchronized和Lock,其他语言也都应该有自己的 ...
- redis客户端、分布式锁及数据一致性
Redis Java客户端有很多的开源产品比如Redission.Jedis.lettuce等. Jedis是Redis的Java实现的客户端,其API提供了比较全面的Redis命令的支持:Redis ...
- Redis系列(二)--分布式锁、分布式ID简单实现及思路
分布式锁: Redis可以实现分布式锁,只是讨论Redis的实现思路,而真的实现分布式锁,Zookeeper更加可靠 为什么使用分布式锁: 单机环境下只存在多线程,通过同步操作就可以实现对并发环境的安 ...
- 在redis上实现分布式锁
/** *在redis上实现分布式锁 */ class RedisLock { private $redisString; private $lockedNames = []; public func ...
- 如何用redis正确实现分布式锁?
先把结论抛出来:redis无法正确实现分布式锁!即使是redis单节点也不行!redis的所谓分布式锁无法用在对锁要求严格的场景下,比如:同一个时间点只能有一个客户端获取锁. 首先来看下单节点下一般r ...
- redis系列:分布式锁
redis系列:分布式锁 1 介绍 这篇博文讲介绍如何一步步构建一个基于Redis的分布式锁.会从最原始的版本开始,然后根据问题进行调整,最后完成一个较为合理的分布式锁. 本篇文章会将分布式锁的实现分 ...
- 一般实现分布式锁都有哪些方式?使用redis如何设计分布式锁?使用zk来设计分布式锁可以吗?这两种分布式锁的实现方式哪种效率比较高?
#(1)redis分布式锁 官方叫做RedLock算法,是redis官方支持的分布式锁算法. 这个分布式锁有3个重要的考量点,互斥(只能有一个客户端获取锁),不能死锁,容错(大部分redis节点创建了 ...
- Redis如何实现分布式锁
今天我们来聊一聊分布式锁的那些事. 相信大家对锁已经不陌生了,我们在多线程环境中,如果需要对同一个资源进行操作,为了避免数据不一致,我们需要在操作共享资源之前进行加锁操作.在计算机科学中,锁(lock ...
随机推荐
- centos7 玩aapt 安卓应用apk解包工具的安装
最近在做一个应用市场的项目,需要在centos7下面对apk解包读取其信息,这就想到了使用Google的解包工具aapt,但是由于中国的原因,国内访问原生工具的地址就有些麻烦,这里就贴出地址:http ...
- http与https差异
HTTPS和HTTP的区别: https协议需要到ca申请证书,一般免费证书很少,需要交费. http是超文本传输协议,信息是明文传输,https 则是具有安全性的ssl加密传输协议 http的连接很 ...
- (C语言)char类型与int类型相加
#include <stdio.h> int main(void) { ; ; int c = a + b; a += b; printf("c=%d",c); //p ...
- 【原】K3Cloud平台开发之Python插件
有时候我们的表单可能很简单,只是一个简单交互的表单,但有可能还是要劳师动众的给它建个工程写个插件,是不是很不爽?例如我有如下一个表单: 功能很简单就是选个业务对象,收集绑定几个字段名,然后确定返回一个 ...
- Learning LexRank——Graph-based Centrality as Salience in Text Summarization(一)
(1)What is Sentence Centrality and Centroid-based Summarization ? Extractive summarization works by ...
- Hadoop 学习笔记 (九) hadoop2.2.0 生产环境部署 HDFS HA部署方法
step1:将安装包hadoop-2.2.0.tar.gz存放到某一个目录下,并解压 step2:修改解压后的目录中的文件夹/etc/hadoop下的xml配置文件(如果文件不存在,则自己创建) 包括 ...
- Powerdesigner数据库建模--概念模型--ER图【转】
转自http://www.cnblogs.com/dekevin/archive/2012/07/18/2596745.html Powerdesigner数据库建模--概念模型--ER图 目标: ...
- Matlab聚类分析[转]
Matlab聚类分析[转] Matlab提供系列函数用于聚类分析,归纳起来具体方法有如下: 方法一:直接聚类,利用clusterdata函数对样本数据进行一次聚类,其缺点为可供用户选择的面较窄,不能更 ...
- Asm.js: Javascript的编译目标
正如许多开发者一样,我也为Asm.js的前景而感到兴奋不已.最近的新闻——Asm.js正 在被Firefox支持——引起了我的兴趣.同样感兴趣的还有Mozilla和Epic声明(mirror)他们已经 ...
- Origin null is not allowed by Access-Control-Allow-Origin
http://www.cnblogs.com/accessking/archive/2012/05/12/2497000.html http://bbs.csdn.net/topics/3903099 ...