您的位置:首页 > 数据库 > Redis

基于Redis实现分布式锁(下)

2017-07-05 18:13 686 查看
基于Redis实现分布式锁,上一篇已经介绍了原理和流程,下面是实现的代码。

Redis分布式锁

package com.github.jedis.lock;

import redis.clients.jedis.Jedis;

/**
* Redis distributed lock implementation.
*/
public class JedisLock {

Jedis jedis;

/**
* Lock key path.
*/
String lockKey;

/**
* Lock expiration in miliseconds.
* 锁超时,防止线程在入锁以后,无限的执行等待
*/
int expireMsecs = 60 * 1000;

/**
* Acquire timeout in miliseconds.
* 锁等待,防止线程饥饿
*/
int timeoutMsecs = 10 * 1000;

boolean locked = false;

/**
* Detailed constructor with default acquire timeout 10000 msecs and lock expiration of 60000 msecs.
*
* @param jedis
* @param lockKey lock key (ex. account:1, ...)
*/
public JedisLock(Jedis jedis, String lockKey) {
this.jedis = jedis;
this.lockKey = lockKey;
}

/**
* Detailed constructor with default lock expiration of 60000 msecs.
*
* @param jedis
* @param lockKey      lock key (ex. account:1, ...)
* @param timeoutMsecs acquire timeout in miliseconds (default: 10000 msecs)
*/
public JedisLock(Jedis jedis, String lockKey, int timeoutMsecs) {
this(jedis, lockKey);
this.timeoutMsecs = timeoutMsecs;
}

/**
* Detailed constructor.
*
* @param jedis
* @param lockKey      lock key (ex. account:1, ...)
* @param timeoutMsecs acquire timeout in miliseconds (default: 10000 msecs)
* @param expireMsecs  lock expiration in miliseconds (default: 60000 msecs)
*/
public JedisLock(Jedis jedis, String lockKey, int timeoutMsecs, int expireMsecs) {
this(jedis, lockKey, timeoutMsecs);
this.expireMsecs = expireMsecs;
}

/**
* Detailed constructor with default acquire timeout 10000 msecs and lock expiration of 60000 msecs.
*
* @param lockKey lock key (ex. account:1, ...)
*/
public JedisLock(String lockKey) {
this(null, lockKey);
}

/**
* Detailed constructor with default lock expiration of 60000 msecs.
*
* @param lockKey      lock key (ex. account:1, ...)
* @param timeoutMsecs acquire timeout in miliseconds (default: 10000 msecs)
*/
public JedisLock(String lockKey, int timeoutMsecs) {
this(null, lockKey, timeoutMsecs);
}

/**
* Detailed constructor.
*
* @param lockKey      lock key (ex. account:1, ...)
* @param timeoutMsecs acquire timeout in miliseconds (default: 10000 msecs)
* @param expireMsecs  lock expiration in miliseconds (default: 60000 msecs)
*/
public JedisLock(String lockKey, int timeoutMsecs, int expireMsecs) {
this(null, lockKey, timeoutMsecs, expireMsecs);
}

/**
* @return lock key
*/
public String getLockKey() {
return lockKey;
}

/**
* Acquire lock.
*
* @return true if lock is acquired, false acquire timeouted
* @throws InterruptedException in case of thread interruption
*/
public synchronized boolean acquire() throws InterruptedException {
return acquire(jedis);
}

/**
* Acquire lock.
*
* @param jedis
* @return true if lock is acquired, false acquire timeouted
* @throws InterruptedException in case of thread interruption
*/
public synchronized boolean acquire(Jedis jedis) throws InterruptedException {
//锁等待,防止线程饥饿
int timeout = timeoutMsecs;
while (timeout >= 0) {
//锁超时,防止线程在入锁以后,无限的执行等待(为什么加1:因为可能有相同时间的操作,这样做不完美,但是实用)
long expires = System.currentTimeMillis() + expireMsecs + 1;
String expiresStr = String.valueOf(expires); //锁到期时间
if (jedis.setnx(lockKey, expiresStr) == 1) {
// lock acquired
locked = true;
return true;
}

String currentValueStr = jedis.get(lockKey); //redis里的时间
if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {
//判断是否为空,不为空的情况下,如果被其他线程设置了值,则第二个条件判断是过不去的
// lock is expired

String oldValueStr = jedis.getSet(lockKey, expiresStr);
//获取上一个锁到期时间,并设置现在的锁到期时间,
//只有一个线程才能获取上一个线上的设置时间,因为jedis.getSet是同步的
if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {
//如过这个时候,多个线程恰好都到了这里,但是只有一个线程的设置值和当前值相同,他才有权利获取锁
// lock acquired
locked = true;
return true;
}
}
timeout -= 100;
Thread.sleep(100);
}
return false;
}

/**
* Acqurired lock release.
*/
publ
db0a
ic synchronized void release() {
release(jedis);
}

/**
* Acqurired lock release.
*/
public synchronized void release(Jedis jedis) {
if (locked) {
jedis.del(lockKey);
locked = false;
}
}
}


分布式锁封装

<pre name="code" class="java">/**
*  Copyright (c)  2011-2020 Panguso, Inc.
*  All rights reserved.
*
*  This software is the confidential and proprietary information of Panguso,
*  Inc. ("Confidential Information"). You shall not
*  disclose such Confidential Information and shall use it only in
*  accordance with the terms of the license agreement you entered into with Panguso.
*/
package com.chinaso.phl.concurrent;

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

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

import com.github.jedis.lock.JedisLock;

/**
* 分布式锁的简单用法
* @author  come from network
* @date 2014-3-7
*/
public class SimpleLock {
private static Logger    logger = LoggerFactory.getLogger(SimpleLock.class);

private static JedisPool pool;
private JedisLock        jedisLock;
private String           lockKey;
private Jedis            jedis;
private int              timeoutMsecs;
private int              expireMsecs;

public SimpleLock(String lockKey) {
this(lockKey, 3000, 300000);
}

public SimpleLock(String lockKey, int timeoutMsecs, int expireMsecs) {
this.lockKey = lockKey;
this.jedis = pool.getResource();
this.timeoutMsecs = timeoutMsecs;
this.expireMsecs = expireMsecs;
this.jedisLock = new JedisLock(jedis, lockKey.intern(), timeoutMsecs, expireMsecs);
}

public void wrap(Runnable runnable) {
long begin = System.currentTimeMillis();
try {
// timeout超时,等待入锁的时间,设置为3秒;expiration过期,锁存在的时间设置为5分钟
logger.info("begin logck,lockKey={},timeoutMsecs={},expireMsecs={}", lockKey, timeoutMsecs, expireMsecs);
if (jedisLock.acquire()) { // 启用锁
runnable.run();
} else {

//logger.info("The time wait for lock more than [{}] ms ", timeoutMsecs);
}
} catch (Throwable t) {
// 分布式锁异常
logger.warn(t.getMessage(), t);
} finally {
this.lockRelease(jedisLock, jedis);
}
logger.info("[{}]cost={}", lockKey, System.currentTimeMillis() - begin);
}

/**
* 释放锁,后期欲将离线计算的释放锁封装
*
* @param lock
* @param jedis
* @author come from network
* @date 2014-3-6
*/
private void lockRelease(JedisLock lock,
Jedis jedis) {
if (lock != null) {
try {
lock.release();// 则解锁
} catch (Exception e) {
}
}
if (jedis != null) {
try {
pool.returnResource(jedis);// 还到连接池里
} catch (Exception e) {
}
}
logger.info("release logck,lockKey={},timeoutMsecs={},expireMsecs={}", lockKey, timeoutMsecs, expireMsecs);
}

public static JedisPool getPool()
{
return pool;
}

public static synchronized void setPool(JedisPool pool)
{
SimpleLock.pool = pool;
}

}


分布式锁测试

package com.chinaso.phl.concurrent;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

import redis.clients.jedis.JedisPool;

/**
* @author com from network
* @date   2014-3-13
*/
public class SimpleLockTest {

/**
* @param args
* @author com from network
* @date   2014-3-13
*/
public static void main(String[] args) {
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
final JedisPool pool = new JedisPool(poolConfig, "192.168.142.237", 6379, 3000); //最后一个参数为密码
SimpleLock.setPool(pool);//只需要初始化一次

String key = "test";
SimpleLock lock = new SimpleLock(key);
lock.wrap(new Runnable() {
@Override
public void run() {
//此处代码是锁上的
System.out.println(111);
}
});
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: