redis setnx实现分布式锁




redis在分布式环境下才需要实现锁,一个客户端下不会出现竞争问题。这种方法对时间同步、锁时间有要求,将锁时间设置到100ms,测试的时候仍然会出现漏加的情况

package 使用setnx_getset;

锁的实现:

package 使用setnx_getset;


import java.io.IOException;


import redis.clients.jedis.JedisCluster;


public class DistributedLockWithSetnxGetset {


private static final long LOCK_TIME = 100;


//setnx这种方法,对LOCK_TIME、以及时间同步有要求,LOCK_TIME太小的话,可能拥有锁的客户端还没来得及释放锁,另外的客户端就通过getset判断锁超时,自己来拿锁操作,导致最后测试的累加值少
public boolean tryLock(JedisCluster jc, String lockKey, long timeout) {


boolean result = false;
long current = System.currentTimeMillis();


while (true) {
// 超时退出
if ((System.currentTimeMillis() - current) / 1000 >= timeout) {
System.out.println("超时退出,获取锁失败 ");
break;
}
// setnx获取锁
long c = System.currentTimeMillis();
if (1 == jc.setnx(lockKey,String.valueOf(c + LOCK_TIME+1))) {
result = true;
//System.out.println(Thread.currentThread().getName()+"获得锁");
jc.incr("count");
break;

//jc.set(key, value, nxxx, expx, time)
if (0 == jc.setnx(lockKey,String.valueOf(c + LOCK_TIME+1))) {

// 锁已经被占用,或者其他客户端异常,等待时间戳判断来释放锁


// 判断超时,直接调用getset会导致锁超时
String t = jc.get(lockKey);
if (t!=null && System.currentTimeMillis() > Long.valueOf(t)) {
// getset判断锁是否过期,先执行getset的客户端获得锁
String preLockValue = jc.getSet(lockKey,String.valueOf(System.currentTimeMillis()+ LOCK_TIME+1));
if (preLockValue != null && Long.valueOf(preLockValue) < System.currentTimeMillis()) {
// if (preLockValue != null && preLockValue.equals(t)) {
//preLockValue==null说明某个客户端把锁删除,为了防止锁竞争,应该让应用重新去尝试获取锁setnx,而不是将锁直接给客户端,否则会出现多个客户端同时获得锁的情况
//System.out.println(Thread.currentThread().getName()+"获得锁");
jc.incr("count");
result = true;
break;


}
} else {
//System.out.println("当前锁仍被占用,sleep 100ms");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}


}


}


return result;
}


}


测试代码:

import java.util.HashSet;
import java.util.Set;


import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;

//这里使用get set值的方法来测试,正常情况下可以直接用redis的incr来实现。如果这种锁的实现没有问题,最后得到的值应用是3000,但测试下来,还是会出现少加的情况
public class ClientTest {

static JedisCluster jc;
private static final String LOCK = "LOCKTEST";
public static void main(String[] args) {
// TODO Auto-generated method stub
String serverInfo = "99.12.226.30:6379,99.12.226.31:6379,99.12.226.32:6379";
Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>();
String ipPort[] = serverInfo.split(",");
for(int i =0 ;i < ipPort.length;i++){
String server[] = ipPort[i].split(":");
jedisClusterNodes.add(new HostAndPort(server[0], Integer.valueOf(server[1])));
}
jc = new JedisCluster(jedisClusterNodes);
jc.set("testvalue","0");
jc.set("count","0");
jc.set("addcount","0");
jc.del(LOCK);
new Thread("1"){


@Override
public void run() {
try{
DistributedLockWithSetnxGetset d1 = new DistributedLockWithSetnxGetset();
for(int i =0;i<1000;i++){
if(d1.tryLock(jc, LOCK, 100)==true){
//jc.set("test",jc.get("test")+"a");
String v = jc.get("testvalue");
//Thread.sleep(3);
jc.set("testvalue", String.valueOf(Long.valueOf(v)+10));
   jc.incr("addcount");
}
}}catch(Exception e){}finally{
/*String t = jc.get(LOCK);
if(t!=null && Long.valueOf(t)>System.currentTimeMillis())*/
jc.del(LOCK);}

}}.start();

new Thread("2"){


@Override
public void run() {
try{
DistributedLockWithSetnxGetset d2 = new DistributedLockWithSetnxGetset();
for(int i =0;i<1000;i++){
if(d2.tryLock(jc, LOCK, 100)==true){
//jc.set("test",jc.get("test")+"a");
String v = jc.get("testvalue");
//Thread.sleep(3);
jc.set("testvalue", String.valueOf(Long.valueOf(v)+10));
jc.incr("addcount");
}
}}catch(Exception e){}finally{
/*String t = jc.get(LOCK);
if(t!=null && Long.valueOf(t)>System.currentTimeMillis())*/
jc.del(LOCK);}

}}.start();

new Thread("3"){


@Override
public void run() {
try{
DistributedLockWithSetnxGetset d3 = new DistributedLockWithSetnxGetset();
for(int i =0;i<1000;i++){
if(d3.tryLock(jc, LOCK, 100)==true){
//jc.set("test",jc.get("test")+"a");
String v = jc.get("testvalue");
//Thread.sleep(3);
jc.set("testvalue", String.valueOf(Long.valueOf(v)+10));
jc.incr("addcount");
}
}}catch(Exception e){}finally{
/*String t = jc.get(LOCK);
if(Long.valueOf(t)>System.currentTimeMillis())*/
jc.del(LOCK);}

}}.start();
}



}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值