简单好用-redis分布式锁实现
RedisLock
package com.pingan.toa.asset.common.utils.redis;
import java.util.Random;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
/**
* @author macow
*/
public class RedisLock {
//加锁标志
public static final String LOCKED = "TRUE";
public static final long ONE_MILLI_NANOS = 1000000L;
//默认超时时间(毫秒)
public static final long DEFAULT_TIME_OUT = 3000;
public static JedisPool pool;
public static final Random r = new Random();
//锁的超时时间(秒),过期删除
public static final int EXPIRE = 5 * 60;
static {
pool = new JedisPool(new JedisPoolConfig(), "host", 6379);
}
private Jedis jedis;
private String key;
//锁状态标志
private boolean locked = false;
public RedisLock(String key) {
this.key = key;
this.jedis = pool.getResource();
}
public boolean lock(long timeout) {
long nano = System.nanoTime();
timeout *= ONE_MILLI_NANOS;
try {
while ((System.nanoTime() - nano) < timeout) {
if (jedis.setnx(key, LOCKED) == 1) {
jedis.expire(key, EXPIRE);
locked = true;
return locked;
}
// 短暂休眠,nano避免出现活锁
Thread.sleep(3, r.nextInt(500));
}
} catch (Exception e) {
}
return false;
}
public boolean lock() {
return lock(DEFAULT_TIME_OUT);
}
// 无论是否加锁成功,必须调用
public void unlock() {
try {
if (locked)
jedis.del(key);
} finally {
pool.returnResource(jedis);
}
}
}
工具类版,修改版
RedisSimpleLockUtils.javapackage com.pingan.toa.asset.common.utils.redis;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.ShardedJedis;
/**
* Redis分布式锁
* @author WEIJIA625
*/
public class RedisSimpleLockUtils {
private static Logger logger = LoggerFactory.getLogger(RedisSimpleLockUtils.class);
// 加锁标志
public static final String LOCKED_TIME = SimpleDateFormat.getDateTimeInstance().format(new Date());
//1毫秒=1000 000纳秒
public static final long ONE_MILLI_NANOS = 1000000L;
// 默认超时时间(毫秒)
public static final long DEFAULT_TIME_OUT = 1 * 1000;
public static final Random r = new Random();
// 锁的超时时间(秒),过期删除
public static final int EXPIRE = 5 * 60;
/**
* 获取分布式锁
* @param lockedKey
* 建议业务名称+业务主键
* @param timeout
* 获取分布式锁的超时时间(毫秒)
* @return
* true:获取锁成功,fasle:获取锁失败
*/
public static boolean lock(String lockedKey,long timeout) {
ShardedJedis shardedJedis = ShardedRedisUtil.getPool().getResource();
try {
long nano = System.nanoTime();
long timeoutNanos =timeout * ONE_MILLI_NANOS;
while ((System.nanoTime() - nano) < timeoutNanos) {
if (shardedJedis.setnx(lockedKey, LOCKED_TIME) == 1) {
shardedJedis.expire(lockedKey, EXPIRE);
logger.info("RedisSimpleLockUtils.lock-->lockedKey=‘{}’ , timeout={}毫秒",lockedKey,timeout);
return true;
}
// 短暂休眠,nano避免出现活锁
Thread.sleep(2, r.nextInt(500));
}
} catch (Exception e) {
ShardedRedisUtil.getPool().returnBrokenResource(shardedJedis);
e.printStackTrace();
} finally {
ShardedRedisUtil.getPool().returnResource(shardedJedis);
}
return false;
}
/**
* 获取分布式锁
* @param lockedKey
* 建议业务名称+业务主键
* @return
* true:获取锁成功,fasle:获取锁失败
*/
public static boolean lock(String lockedKey) {
return lock(lockedKey,6);
}
/**
* 获取调度锁
* 注意点:1.无需释放锁;2.任务调度周期>5分钟 && 任务处理耗时 > 1秒
* @param lockedKey
* 调度锁的key
* @return
* true:获取锁成功,fasle:获取锁失败
*
* @desc
* instanceMaxDiffTime:集群下各实例所在应用服务器的最大时间差
* timeout:超时时间,默认为1秒
* tasktime:任务执行所需时间
* expireTime:锁有效时间,默认为5分钟
* scheduletime:调度周期
* 调度锁有效条件: 0<=instanceMaxDiffTime<timeout<tasktime<expireTime<scheduletime
* 此方法默认不调用解锁下,锁的有效条件:1秒<tasktime<5分钟<scheduletime
*/
public static boolean scheduleLock(String lockedKey) {
return lock(lockedKey,DEFAULT_TIME_OUT);
}
/**
* 释放分布式锁
* @param lockedKey
* 建议业务名称+业务主键
*/
public static void unlock(String lockedKey) {
ShardedJedis shardedJedis = ShardedRedisUtil.getPool().getResource();
try {
Long del = shardedJedis.del(lockedKey);
logger.info("RedisSimpleLockUtils.unlock-->lockedKey=‘{}’ ,del lock={}",lockedKey,del);
}catch (Exception e) {
ShardedRedisUtil.getPool().returnBrokenResource(shardedJedis);
} finally {
ShardedRedisUtil.getPool().returnResource(shardedJedis);
}
}
/**
* 测试辅助类
* @author WEIJIA625
*
*/
static class TaskWork{
public void scheduleWork(){
if(!RedisSimpleLockUtils.scheduleLock("doWork")) {return;}
logger.info("@@@---@@@>{} dowork start",Thread.currentThread().getName());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.info("###---###>{} dowork end",Thread.currentThread().getName());
}
public void secondKill(){
if(!RedisSimpleLockUtils.lock("doWork")) {return;}
logger.info("@@@---@@@>{} dowork start",Thread.currentThread().getName());
try {
Thread.sleep(new Random().nextInt(50));
logger.info("###---###>{} dowork end",Thread.currentThread().getName());
} catch (Exception e) {
e.printStackTrace();
}finally{
RedisSimpleLockUtils.unlock("doWork");
}
}
}
public static void main(String[] args) {
//测试类见:RedisSimpleLockUtilsTest.java
}
}
测试类
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.pingan.toa.asset.common.utils.redis.RedisSimpleLockUtils.TaskWork;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/spring-context.xml")
public class RedisSimpleLockUtilsTest {
@Test
public void testScheduleWork() {
ExecutorService pool= Executors.newFixedThreadPool(20);
final TaskWork task=new TaskWork();
for(int i=0;i<10;i++){
pool.execute(new Runnable() {
@Override
public void run() {
while (true) {
task.scheduleWork();
}
}
});
}
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Test
public void testSecondKill() {
ExecutorService pool= Executors.newFixedThreadPool(50);
final TaskWork task=new TaskWork();
for(int i=0;i<10;i++){
pool.execute(new Runnable() {
@Override
public void run() {
while (true) {
task.secondKill();;
}
}
});
}
try {
Thread.sleep(100000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}