Redis实现分布式锁
大家都知道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..."); 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..."); 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
全文完,如果本文对您有所帮助,请花 1 秒钟帮忙点击一下广告,谢谢。
作 者: BridgeLi,https://www.bridgeli.cn
原文链接:http://www.bridgeli.cn/archives/326
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。
作 者: BridgeLi,https://www.bridgeli.cn
原文链接:http://www.bridgeli.cn/archives/326
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。
近期评论