概述
可参考以下网站 Redisson Github WIKI
Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。提供了使用Redis的最简单和最便捷的方法。促进使用者对Redis的关注分离。并且底层采用的是Netty框架。
我的理解:基于Netty封装了一些利用了Redis特性的工具,让我们可以更高效、更简便的使用Redis。
本文主要介绍,如果在一个springboot项目中接入并使用Redisson网络提供的可重入锁(Reentrant Lock),特别注意:这只是Redisson整个框架中的一小部分。
如何使用
说明:该项目是Springboot(1.5.10.RELEASE),其中已经集成了Redis(如何集成这个就不多介绍这个了= =),以下仅演示接入并使用Redisson的一种方式,更多接入方式
- 引入maven依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.5.0</version>
</dependency>
- 配置文件
redisson:
address: redis://${spring.redis.host}:${spring.redis.port} # redis地址
timeout: 3000 # 超时时间
connectionPoolSize: 64 # 连接池大小 默认64
connectionMinimumIdleSize: 32 # 最小空闲连接数 默认32
使用单机模式Redis
- 配置类(Redis集成配置已省略)
package com.distributedlock.config;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RedissonConfig {
/**
* redis连接地址
*/
@Value("${redisson.address}")
private String redissonAddress;
/**
* redis连接密码
*/
@Value("${spring.redis.password}")
private String password;
/**
* redis连接时间
*/
@Value("${redisson.timeout}")
private Integer timeout;
/**
* redis连接池大小
*/
@Value("${redisson.connectionPoolSize}")
private Integer connectionPoolSize;
/**
* redis最小连接空闲数
*/
@Value("${redisson.connectionMinimumIdleSize}")
private Integer connectionMinimumIdleSize;
/**
* 获取单机模式配置
* @return
*/
@Bean
public RedissonClient getRedissonClient() {
Config config = new Config();
config.useSingleServer()
.setAddress(redissonAddress)
.setPassword(password)
.setTimeout(timeout)
.setConnectionPoolSize(connectionPoolSize)
.setConnectionMinimumIdleSize(connectionMinimumIdleSize);
return Redisson.create(config);
}
- 封装接口和实现类(方便使用)
接口:定义一些要用到的方法,可通过实现类来根据需求重写方法
package com.distributedlock.lock.redisson;
import java.util.concurrent.TimeUnit;
/**
* redisson分布式锁
* 注意:
* 1. 可自定义锁过期时间、获取锁间隔时间、获取锁超时时间、获取锁重试次数
* 2. 如未设置以上参数,则使用默认值
*/
public interface IRedissionLock {
boolean tryLock(String key);
boolean tryLock(String key, long waitTime);
boolean tryLock(String key, long waitTime, long sleepTime);
boolean tryLock(String key, long waitTime, long sleepTime, TimeUnit timeUnit);
void unLock(String key);
}
接口实现类:
package com.distributedlock.lock.redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
@Component
public class RedssionLockImpl implements IRedissionLock{
@Autowired
private RedissonClient redissonClient;
/**
* 获取锁超时时间
*/
private static final long waitTime = 50000L;
/**
* 锁过期时间
* 单个线程拥有锁的最长时间(有自动续期的逻辑)
*/
private static final long leaseTime = 20000L;
private static TimeUnit timeUnit = TimeUnit.MILLISECONDS;
private static final ConcurrentMap<String, RLock> rLockConcurrentMap = new ConcurrentHashMap<>();
@Value("${redisson.lockKeyPre}")
private String lockKeyPre;
@Override
public boolean tryLock(String key) {
return this.tryLock(key, waitTime, leaseTime, timeUnit);
}
@Override
public boolean tryLock(String key, long leaseTime) {
return this.tryLock(key, waitTime, leaseTime, timeUnit);
}
@Override
public boolean tryLock(String key, long leaseTime, long sleepTime) {
return this.tryLock(key, waitTime, sleepTime, timeUnit);
}
@Override
public boolean tryLock(String key, long waitTime, long leaseTime, TimeUnit timeUnit) {
RLock rLock = this.getRLock(lockKeyPre + key);
try {
return rLock.tryLock(waitTime, leaseTime, timeUnit);
} catch (Exception e) {
return false;
}
}
@Override
public void unLock(String key) {
this.getRLock(lockKeyPre + key).unlock();
}
private RLock getRLock(String objectName) {
String saveKey = Thread.currentThread().getId() + ":" + objectName;
RLock rLock = rLockConcurrentMap.get(saveKey);
if (rLock == null) {
rLock = this.newRLock(objectName);
rLockConcurrentMap.put(saveKey, rLock);
}
return rLock;
}
private RLock newRLock(String key) {
return redissonClient.getLock(key);
}
}
- 使用示例
package com.distributedlock;
import com.distributedlock.lock.redisson.IRedissionLock;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@RunWith(SpringRunner.class)
@SpringBootTest
public class RedissonLockTest {
@Autowired
private IRedissionLock redssionLock;
private Logger logger = LoggerFactory.getLogger(this.getClass());
private Integer sum = 0;
/**
* redisLock demo
*
* @throws InterruptedException
*/
@Test
public void test() throws InterruptedException {
logger.info("redis-lock-test: client start! ");
ExecutorService executorService = Executors.newCachedThreadPool();
//同步工具类
CountDownLatch latch = new CountDownLatch(100);
for (int i = 0; i < 100; i++) {
//从线程池中获取100个线程,并执行
executorService.submit(new MyLock("client-" + i, latch));
logger.info("client-" + i + " init completed !");
}
//关闭线程池
executorService.shutdown();
latch.await();
logger.info("redis-lock-test: all client completed -> sum: {} ", sum);
}
class MyLock implements Runnable {
private String name;
private CountDownLatch latch;
public MyLock(String name, CountDownLatch latch) {
this.name = name;
this.latch = latch;
}
@Override
public void run() {
String key = "root";
try {
if (redssionLock.tryLock(key)) {
sum += 1;
logger.info(this.name + " get lock success key: " + key + " work -> sum = " + sum);
} else {
logger.error(this.name + " get lock key: " + key + " timeout!");
}
latch.countDown();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
redssionLock.unLock(key);
logger.info(this.name + " release " + key);
} catch (Exception e) {
logger.error(this.name + " release " + key + " error");
}
}
}
}
}
此demo启动了100线程分别对变量sum进行一次加1操作,运行后可发现sum的值刚好是100。(如果没有加锁,会出现并发情况,最后的值不是100的现象)
原理分析
可关注我的另一篇博客(之后会更新)
收获
因为有自己实现过简单的redis分布式锁,所以在理解并使用Redisson分布式锁的过程中是比较轻松的。虽然实现原理大同小异,但是与Redisson却能把整个过程实现的更加完美,在阅读其中源码的时候我发现了,其中使用到了有很多的设计模式和一些很巧妙的编码模式,能有很多的收获。
对于自己的编码习惯也会有一些改变,会尽量像框架源码里的那样编排自己的代码,例如这里用到了ConcurrentHashMap来存储线程中实例化过的锁,还有用到了单例模式中的饿汉模式来实现获取锁实例化对象。尽管都是很细微的地方,但是自己会一直注意努力的,Hhhh
来源:CSDN
作者:Java界的齐达内
链接:https://blog.csdn.net/zhuolou1208/article/details/103951386