网上有很多使用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(); } } }
假如不加锁的话,各个线程的日志会混乱地输出在一起;
使用锁后,一个线程日志输出完后才会输出另一个线程的,说明锁起作用了。