网上有很多使用jedis实现分布式锁的文章了。redis官方推荐的分布式锁实现是基于redisson的。之前一直使用别人写的基于jedis的封装,今天自己动手写了一个简单的redisson的封装实现,Mark一下。
1、定义回调接口
package com.ijavoracle.common.lock;
/**
* 分布式锁回调接口
*
* @author ijavoracle
*/
public interface DistributedLockCallback<T> {
/**
* 调用者必须在此方法中实现需要加分布式锁的业务逻辑
*
* @return
*/
public T process();
/**
* 得到分布式锁名称
*
* @return
*/
public String getLockName();
}
2、定义分布式锁模板接口
package com.ijavoracle.common.lock;
import java.util.concurrent.TimeUnit;
/**
* 分布式锁操作模板
*
* @author ijavoracle
*/
public interface DistributedLockTemplate {
/**
* 使用分布式锁,使用锁默认超时时间。
*
* @param callback
* @return
*/
public <T> T lock(DistributedLockCallback<T> callback);
/**
* 使用分布式锁。自定义锁的超时时间
*
* @param callback
* @param leaseTime 锁超时时间。超时后自动释放锁。
* @param timeUnit
* @return
*/
public <T> T lock(DistributedLockCallback<T> callback, long leaseTime, TimeUnit timeUnit);
}
3、使用redisson最简单的Single instance mode实现分布式锁模板接口
package com.ijavoracle.common.lock;
import java.util.concurrent.TimeUnit;
import org.redisson.RedissonClient;
import org.redisson.core.RLock;
/**
* Single Instance mode 分布式锁模板
*
* @author ijavoracle
*/
public class SingleDistributedLockTemplate implements DistributedLockTemplate {
private static final long DEFAULT_TIMEOUT = 5;
private static final TimeUnit DEFAULT_TIME_UNIT = TimeUnit.SECONDS;
private RedissonClient redisson;
public SingleDistributedLockTemplate() {
}
public SingleDistributedLockTemplate(RedissonClient redisson) {
this.redisson = redisson;
}
@Override
public <T> T lock(DistributedLockCallback<T> callback) {
return lock(callback, DEFAULT_TIMEOUT, DEFAULT_TIME_UNIT);
}
@Override
public <T> T lock(DistributedLockCallback<T> callback, long leaseTime, TimeUnit timeUnit) {
RLock lock = null;
try {
lock = redisson.getLock(callback.getLockName());
lock.lock(leaseTime, timeUnit);
return callback.process();
} finally {
if (lock != null) {
lock.unlock();
}
}
}
public void setRedisson(RedissonClient redisson) {
this.redisson = redisson;
}
}
4、定义spring的FactoryBean
package com.ijavoracle.common.lock;
import java.io.IOException;
import java.io.InputStream;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.redisson.Config;
import org.redisson.Redisson;
import org.redisson.RedissonClient;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.util.Assert;
/**
* 创建分布式锁模板实例的工厂Bean
*
* @author ijavoracle
*/
public class DistributedLockFactoryBean implements FactoryBean<DistributedLockTemplate> {
private Logger logger = Logger.getLogger(DistributedLockFactoryBean.class);
private LockInstanceMode mode;
private DistributedLockTemplate distributedLockTemplate;
private RedissonClient redisson;
@PostConstruct
public void init() {
logger.debug("初始化分布式锁模板");
InputStream inputStream = null;
Config config = null;
try {
inputStream = DistributedLockFactoryBean.class.getClassLoader().getResourceAsStream("redisson-conf.json");
config = Config.fromJSON(inputStream);
} catch (IOException e) {
logger.error("读取Redisson配置失败", e);
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
logger.error("", e);
}
}
}
Assert.notNull(config);
redisson = Redisson.create(config);
}
@PreDestroy
public void destroy() {
logger.debug("销毁分布式锁模板");
redisson.shutdown();
}
@Override
public DistributedLockTemplate getObject() throws Exception {
switch (mode) {
case SINGLE:
distributedLockTemplate = new SingleDistributedLockTemplate(redisson);
break;
}
return distributedLockTemplate;
}
@Override
public Class<?> getObjectType() {
return DistributedLockTemplate.class;
}
@Override
public boolean isSingleton() {
return true;
}
public void setMode(String mode) {
if (StringUtils.isBlank(mode)) {
throw new IllegalArgumentException("未找到dlm.redisson.mode配置项");
}
this.mode = LockInstanceMode.parse(mode);
if (this.mode == null) {
throw new IllegalArgumentException("不支持的分布式锁模式");
}
}
private enum LockInstanceMode {
SINGLE;
public static LockInstanceMode parse(String name) {
for (LockInstanceMode modeIns : LockInstanceMode.values()) {
if (modeIns.name().equals(name.toUpperCase())) {
return modeIns;
}
}
return null;
}
}
}
redisson-conf.json是从redisson官方直接copy过来配置信息(只改了redis服务器IP/PORT)
{ "singleServerConfig":{ "idleConnectionTimeout":10000, "pingTimeout":1000, "connectTimeout":1000, "timeout":1000, "retryAttempts":3, "retryInterval":1000, "reconnectionTimeout":3000, "failedAttempts":3, "password":123456, "subscriptionsPerConnection":5, "clientName":null, "address":[ "//10.33.40.83:6379" ], "subscriptionConnectionMinimumIdleSize":1, "subscriptionConnectionPoolSize":25, "connectionMinimumIdleSize":5, "connectionPoolSize":100, "database":0, "dnsMonitoring":false, "dnsMonitoringInterval":5000 }, "threads":0, "codec":null, "useLinuxNativeEpoll":false, "eventLoopGroup":null }
5、配置pom.xml依赖
<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.16</version> </dependency> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>2.2.15</version> </dependency>
6、配置FactoryBean
<bean id="distributeLockTemplate" class="com.ijavoracle.common.lock.DistributedLockFactoryBean"> <property name="mode" value="SINGLE"/> </bean>
7、测试
package com.ijavoracle.common.lock;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.hikvision.hbss.common.lock.DistributedLockCallback;
import com.hikvision.hbss.common.lock.DistributedLockTemplate;
public class DistributedLockTest {
private static DistributedLockTemplate distributedLockTemplate;
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/spring/common.xml");
distributedLockTemplate = ctx.getBean("distributeLockTemplate", DistributedLockTemplate.class);
CountDownLatch startSignal = new CountDownLatch(1);
CountDownLatch doneSignal = new CountDownLatch(5);
for (int i = 0; i < 5; ++i) { // create and start threads
new Thread(new Worker(startSignal, doneSignal)).start();
}
startSignal.countDown(); // let all threads proceed
doneSignal.await();
System.out.println("All processors done. Shutdown connection");
ctx.close();
}
static class Worker implements Runnable {
private final CountDownLatch startSignal;
private final CountDownLatch doneSignal;
Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
this.startSignal = startSignal;
this.doneSignal = doneSignal;
}
public void run() {
try {
startSignal.await();
distributedLockTemplate.lock(new DistributedLockCallback<Object>() {
@Override
public Object process() {
doTask();
return null;
}
@Override
public String getLockName() {
return "MyLock";
}
});
} catch (InterruptedException ex) {
} // return;
}
void doTask() {
System.out.println(Thread.currentThread().getName() + " start");
Random random = new Random();
int _int = random.nextInt(200);
System.out.println(Thread.currentThread().getName() + " sleep " + _int + "millis");
try {
Thread.sleep(_int);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " end");
doneSignal.countDown();
}
}
}
假如不加锁的话,各个线程的日志会混乱地输出在一起;
使用锁后,一个线程日志输出完后才会输出另一个线程的,说明锁起作用了。