redis分布式锁实现控制并发
2017-06-30 17:07
232 查看
最近项目有一个关于征信协议生成pdf,要求客户签订的第一份协议生成pdf,由于是服务器集群,所以要用到分布式锁的概念。
写完后,写了个例子总结了下,直接上例子
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang.math.RandomUtils;
import org.springframework.data.redis.core.TimeoutUtils;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
public class Test {
static Set<HostAndPort> haps_dest_dev = new HashSet<HostAndPort>();
static JedisCluster jedisCluster;
static {
haps_dest_dev.add(new HostAndPort("172.29.0.97", 6379));
haps_dest_dev.add(new HostAndPort("172.29.0.97", 6389));
haps_dest_dev.add(new HostAndPort("172.29.0.97", 6399));
haps_dest_dev.add(new HostAndPort("172.29.0.98", 6379));
haps_dest_dev.add(new HostAndPort("172.29.0.98", 6389));
haps_dest_dev.add(new HostAndPort("172.29.0.98", 6399));
jedisCluster = new JedisCluster(haps_dest_dev);
}
/** 毫秒与毫微秒的换算单位 1毫秒 = 1000000毫微秒 */
public static final long MILLI_NANO_CONVERSION = 1000 * 1000L;
/** 默认超时时间(毫秒) */
public static final long DEFAULT_TIME_OUT = 1000;
// private volatile boolean locked = false;
/**
* 获取锁
* @param timeout 获取锁的等待时间 毫秒
* @param expireSecs 锁的有效时间,必须大于0 毫秒
* @return
*/
private boolean lock(String lockKey, long timeout, int expireSecs) throws InterruptedException {
if (expireSecs <= 0) {
throw new IllegalArgumentException("Param expireSecs must lager than zero.");
}
long nano = System.nanoTime();
timeout *= MILLI_NANO_CONVERSION;
try {
while ((System.nanoTime() - nano) < timeout) {
long expires = System.currentTimeMillis() + expireSecs + 1;
// 5秒之后锁到期
String expiresStr = String.valueOf(expires); //锁到期时间
// 获取到锁
if (jedisCluster.setnx(lockKey, String.valueOf(expiresStr)) == 1) {
// lock acquired
// locked = true;
jedisCluster.expire(lockKey, (int) TimeoutUtils.toSeconds(expireSecs, TimeUnit.MILLISECONDS));
return true;
}
// 没有获取到锁
String currentValueStr = jedisCluster.get(lockKey);
// expireMsecs(5秒)锁的有效期内无法进入if判断,如果锁超时了
if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {
//判断是否为空,不为空的情况下,如果被其他线程设置了值,则第二个条件判断是过不去的
// lock is expired
// 如果锁超时重新设置
String oldValueStr = jedisCluster.getSet(lockKey, expiresStr);
//获取上一个锁到期时间,并设置现在的锁到期时间,
//只有一个线程才能获取上一个线上的设置时间,因为jedis.getSet是同步的
// 值相同说明是同一个线程的操作,获取锁成功
if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {
//防止误删(覆盖,因为key是相同的)了他人的锁——这里达不到效果,这里值会被覆盖,但是因为什么相差了很少的时间,所以可以接受
//如过这个时候,多个线程恰好都到了这里,但是只有一个线程的设置值和当前值相同,他才有权利获取锁
// lock acquired
// locked = true;
jedisCluster.expire(lockKey, (int) TimeoutUtils.toSeconds(expireSecs, TimeUnit.MILLISECONDS));
return true;
} else {
// 被其他线程抢先获取锁
// locked = false;
}
}
// 锁没有超时,继续等待
}
Thread.sleep(3, RandomUtils.nextInt(500));
} catch (Exception e) {
throw new RuntimeException("Locking error", e);
}
return false;
}
/**
* 释放锁
*/
public void releaseLock(String lockKey){
try {
long current = System.currentTimeMillis();
// 避免删除非自己获取得到的锁
if (current < Long.valueOf(jedisCluster.get(lockKey)))
jedisCluster.del(lockKey);
// locked = false;
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Test t = new Test();
try {
if (t.lock("lock:aa", DEFAULT_TIME_OUT, 5000)) {
System.out.println("获取锁");
} else {
System.out.println("未获取锁");
}
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
写完后,写了个例子总结了下,直接上例子
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang.math.RandomUtils;
import org.springframework.data.redis.core.TimeoutUtils;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
public class Test {
static Set<HostAndPort> haps_dest_dev = new HashSet<HostAndPort>();
static JedisCluster jedisCluster;
static {
haps_dest_dev.add(new HostAndPort("172.29.0.97", 6379));
haps_dest_dev.add(new HostAndPort("172.29.0.97", 6389));
haps_dest_dev.add(new HostAndPort("172.29.0.97", 6399));
haps_dest_dev.add(new HostAndPort("172.29.0.98", 6379));
haps_dest_dev.add(new HostAndPort("172.29.0.98", 6389));
haps_dest_dev.add(new HostAndPort("172.29.0.98", 6399));
jedisCluster = new JedisCluster(haps_dest_dev);
}
/** 毫秒与毫微秒的换算单位 1毫秒 = 1000000毫微秒 */
public static final long MILLI_NANO_CONVERSION = 1000 * 1000L;
/** 默认超时时间(毫秒) */
public static final long DEFAULT_TIME_OUT = 1000;
// private volatile boolean locked = false;
/**
* 获取锁
* @param timeout 获取锁的等待时间 毫秒
* @param expireSecs 锁的有效时间,必须大于0 毫秒
* @return
*/
private boolean lock(String lockKey, long timeout, int expireSecs) throws InterruptedException {
if (expireSecs <= 0) {
throw new IllegalArgumentException("Param expireSecs must lager than zero.");
}
long nano = System.nanoTime();
timeout *= MILLI_NANO_CONVERSION;
try {
while ((System.nanoTime() - nano) < timeout) {
long expires = System.currentTimeMillis() + expireSecs + 1;
// 5秒之后锁到期
String expiresStr = String.valueOf(expires); //锁到期时间
// 获取到锁
if (jedisCluster.setnx(lockKey, String.valueOf(expiresStr)) == 1) {
// lock acquired
// locked = true;
jedisCluster.expire(lockKey, (int) TimeoutUtils.toSeconds(expireSecs, TimeUnit.MILLISECONDS));
return true;
}
// 没有获取到锁
String currentValueStr = jedisCluster.get(lockKey);
// expireMsecs(5秒)锁的有效期内无法进入if判断,如果锁超时了
if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {
//判断是否为空,不为空的情况下,如果被其他线程设置了值,则第二个条件判断是过不去的
// lock is expired
// 如果锁超时重新设置
String oldValueStr = jedisCluster.getSet(lockKey, expiresStr);
//获取上一个锁到期时间,并设置现在的锁到期时间,
//只有一个线程才能获取上一个线上的设置时间,因为jedis.getSet是同步的
// 值相同说明是同一个线程的操作,获取锁成功
if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {
//防止误删(覆盖,因为key是相同的)了他人的锁——这里达不到效果,这里值会被覆盖,但是因为什么相差了很少的时间,所以可以接受
//如过这个时候,多个线程恰好都到了这里,但是只有一个线程的设置值和当前值相同,他才有权利获取锁
// lock acquired
// locked = true;
jedisCluster.expire(lockKey, (int) TimeoutUtils.toSeconds(expireSecs, TimeUnit.MILLISECONDS));
return true;
} else {
// 被其他线程抢先获取锁
// locked = false;
}
}
// 锁没有超时,继续等待
}
Thread.sleep(3, RandomUtils.nextInt(500));
} catch (Exception e) {
throw new RuntimeException("Locking error", e);
}
return false;
}
/**
* 释放锁
*/
public void releaseLock(String lockKey){
try {
long current = System.currentTimeMillis();
// 避免删除非自己获取得到的锁
if (current < Long.valueOf(jedisCluster.get(lockKey)))
jedisCluster.del(lockKey);
// locked = false;
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Test t = new Test();
try {
if (t.lock("lock:aa", DEFAULT_TIME_OUT, 5000)) {
System.out.println("获取锁");
} else {
System.out.println("未获取锁");
}
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
相关文章推荐
- 如何利用Redis分布式锁实现控制并发
- 如何利用Redis分布式锁实现控制并发
- SQL Server 2000+ADO.NET实现并发控制
- 实现 Java 多线程并发控制框架
- SQL Server 2000+ADO.NET实现并发控制...(一)
- 【多线程】控制多线程并发的函数的阻塞功能如何实现?
- 数据库应用中并发控制若干实现途径
- SQL Server 2000+ADO.NET实现并发控制
- 数据库应用中并发控制若干实现途径
- 实现 Java 多线程并发控制框架--转载
- 利用CAS机制实现多进程,多线程下的无锁并发控制
- Web应用中并发控制的实现
- SQL Server 2000+ADO.NET实现并发控制
- Web应用中并发控制的实现
- SQL Server 2000+ADO.NET实现并发控制.
- 实现 Java 多线程并发控制框架
- 实现控件的并发控制
- 在CGD中快速实现多线程的并发控制
- 实现 Java 多线程并发控制框架
- SQL Server 2000+ADO.NET实现并发控制...(三)