大家都知道Redis是NoSQL的一种,目前在互联网公司中在作为缓存广泛的使用者,其实利用Redis的setnx还可以快速实现一个分布式锁,公司的业务就需要使用分布式锁保证数据的唯一性,经检索在网上发现已经有活雷锋分享了一套,本着不在重新发明轮子的想法,测试了一下好像没有问题,几乎不用对原代码进行修改,就能使用,下面就分享在这里,供需要的朋友参考。原文里面还有对实现的原理进行解释,所以本文就不再赘述,详情请通过参考资料进行访问。


package cn.bridgeli.distributedlock;

import java.util.UUID;  
import java.util.concurrent.CountDownLatch;  
import java.util.concurrent.ExecutorService;  
import java.util.concurrent.Executors;  
import java.util.concurrent.atomic.AtomicInteger;

import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;

import redis.clients.jedis.Jedis;  
import redis.clients.jedis.JedisPool;  
import redis.clients.jedis.JedisPoolConfig;  
import redis.clients.jedis.Transaction;

public class RedisDistributedLock {

private static final Logger LOG = LoggerFactory.getLogger(RedisDistributedLock.class);

private static final String redisHost = "127.0.0.1";

private static final int port = 6381;

private static JedisPoolConfig config;

private static JedisPool pool;

private static ExecutorService service;

private static int ThLeng = 10;

private static CountDownLatch latch;

private static AtomicInteger Countor = new AtomicInteger(0);

private static int count = 0;

private static String LockName = "mylock_test10";

static {  
// 利用Redis连接池,保证多个线程利用多个连接,充分模拟并发性  
config = new JedisPoolConfig();  
config.setMaxIdle(10);  
config.setMaxWaitMillis(1000);  
config.setMaxTotal(30);  
pool = new JedisPool(config, redisHost, port);  
// 利用ExecutorService 管理线程  
service = Executors.newFixedThreadPool(ThLeng);  
// CountDownLatch保证主线程在全部线程结束之后退出  
latch = new CountDownLatch(ThLeng);  
}

/**  
* 获取锁 tips:生成一个UUID,作为Key的标识,不断轮询lockName,直到set成功,表示成功获取锁。  
* 其他的线程在set此lockName时被阻塞直到超时。  
*  
* @param pool  
* @param lockName  
* @param timeouts  
* @return 锁标志  
*/  
public static String getLock(JedisPool pool, String lockName, long timeouts) {  
Jedis client = pool.getResource();  
try {  
String value = UUID.randomUUID().toString();  
long timeWait = System.currentTimeMillis() + timeouts * 1000;  
while (System.currentTimeMillis() < timeWait) {  
if (client.setnx(lockName, value) == 1) {  
// 防止key卡死,一直不释放锁,所以1800s过期  
client.expire(lockName, 1800);  
LOG.info("lock geted");  
return value;  
}  
try {  
Thread.sleep(1000);  
} catch (InterruptedException e) {  
e.printStackTrace();  
}  
}  
LOG.info("get lock timeouts");  
} finally {  
// pool.returnBrokenResource(client);  
pool.returnResource(client);  
}  
return null;  
}

/**  
* 释放锁 tips:对lockName做watch,开启一个事务,删除以LockName为key的锁,删除后,此锁对于其他线程为可争抢的。  
*  
* @param pool  
* @param lockName  
* @param value  
*/  
public static void relaseLock(JedisPool pool, String lockName, String value) {  
Jedis client = pool.getResource();  
try {  
while (true) {  
client.watch(lockName);  
if (client.get(lockName).equals(value)) {  
Transaction tx = client.multi();  
tx.del(lockName);  
tx.exec();  
return;  
}  
client.unwatch();  
}  
} finally {  
// pool.returnBrokenResource(client);  
pool.returnResource(client);  
}  
}

public static void main(String args[]) {  
for (int i = 0; i < ThLeng; i++) {  
String tName = "thread-" + i;  
Thread t = new Thread(new SubAddThread(pool, tName));  
LOG.info(tName + "inited&#8230;");  
service.submit(t);  
}  
service.shutdown();  
try {  
latch.await();  
} catch (InterruptedException e) {  
e.printStackTrace();  
}  
LOG.info(Countor.get() + "");  
LOG.info(count + "");  
}

public static class SubAddThread implements Runnable {

private String name;

private JedisPool pool;

public SubAddThread(JedisPool pool, String uname) {  
this.pool = pool;  
this.name = uname;  
}

@Override  
public void run() {  
for (int i = 0; i < 100; i++) {  
LOG.info(name + " starting&#8230;");  
String valuse = getLock(pool, LockName, 50);  
LOG.info(name + " get Lock " + valuse);  
count++;  
relaseLock(pool, LockName, valuse);  
Countor.incrementAndGet();  
LOG.info(name + " " + count);  
}  
latch.countDown();  
LOG.info(name + " complated");  
}

}  
}

参考资料:http://www.jianshu.com/p/c1f5d26cb1c9