Reddission 简单讲解
Reddission
其他好文推荐:
1、Redisson 使用手册
https://www.mianshigee.com/tutorial/redisson-wiki-zh/spilt.1.7.-分布式集合.md
2、从头开始学Redisson
https://blog.csdn.net/yanluandai1985/category_9795024.html
从头开始学Redisson--------分布式集合之映射(RMap)
https://zhoutianyu.blog.csdn.net/article/details/105048802
从头开始学Redisson--------分布式集合之本地缓存映射(RLocalCachedMap)
https://blog.csdn.net/yanluandai1985/article/details/105096869/
什么是Reddission
Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务。包括Queue、Lock、AtomicLong、CountDownLatch、Publish / Subscribe、Executor service等,提供了使用Redis的最简单和最便捷的方法。Redisson的宗旨是促进使用者对Redis的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上。Redisson底层采用的是Netty框架,支持Redis2.8以上版本,支持Java1.6+以上版本。
Jedis与Redisson
-
Jedis:Jedis 只是简单的封装了 Redis 的API库,可以看作是Redis客户端,它的方法和Redis 的命令很类似,相比于Redisson 更原生一些,更灵活。
-
Redisson:Redisson 不仅封装了 redis ,还封装了对更多数据结构的支持,以及锁等功能,相比于Jedis 更加大。
Redisson帮助文档
wiki:https://github.com/redisson/redisson/wiki/目录
常用功能介绍
-
分布式锁
1、可重入锁:以线程为单位,当一个线程获取对象锁之后,这个线程可以再次获取本对象上的锁,而其他的线程是不可以的
2、公平锁:每个线程抢占锁的顺序为先后调用lock方法的顺序依次获取锁,类似于排队吃饭
3、闭锁:基于Redisson的Redisson分布式闭锁(CountDownLatch)Java对象RCountDownLatch采用了与java.util.concurrent.CountDownLatch相似的接口和用法
-
限流器
基于Redis的分布式限流器(RateLimiter)可以用来在分布式环境下现在请求方的调用频率。既适用于不同Redisson实例下的多线程限流,也适用于相同Redisson实例下的多线程限流。该算法不保证公平性。除了同步接口外,还提供了异步(Async)、反射式(Reactive)和RxJava2标准的接口。
-
话题(订阅分发)
订阅话题后当接收到消息进行消费
-
事务
为RMap、RMapCache、RLocalCachedMap、RSet、RSetCache和RBucket这样的对象提供了具有ACID属性的事务功能Redisson事务通过分布式锁保证了连续写入的原子性,同时在内部通过操作指令队列实现了Redis原本没有的提交与滚回功能当提交与滚回遇到问题的时候,将通过org.redisson.transaction.TransactionException告知用户
-
队列
有界无界、阻塞非阻塞队列
-
分布式服务
分布式远程服务:可以用来通过共享接口执行存在于另一个Redisson实例里的对象方法。换句话说就是通过Redis实现了Java的远程过程调用(RPC)。分布式远程服务基于可以用POJO对象,方法的参数和返回类不受限制,可以是任何类型。
执行任务服务:Redisson的分布式执行服务实现了java.util.concurrent.ExecutorService接口,支持在不同的独立节点里执行基于java.util.concurrent.Callable接口或java.lang.Runnable接口或Lambda的任务。这样的任务也可以通过使用Redisson实例,实现对储存在Redis里的数据进行操作。Redisson分布式执行服务是最快速和有效执行分布式运算的方法。
调度任务服务:Redisson的分布式调度任务服务实现了java.util.concurrent.ScheduledExecutorService接口,支持在不同的独立节点里执行基于java.util.concurrent.Callable接口或java.lang.Runnable接口的任务。Redisson独立节点按顺序运行Redis列队里的任务。调度任务是一种需要在未来某个指定时间运行一次或多次的特殊任务。
-
布隆过滤器
本质上布隆过滤器是一种数据结构,比较巧妙的概率型数据结构(probabilistic data structure),特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”。相比于传统的 List、Set、Map 等数据结构,它更高效、占用空间更少,但是缺点是其返回的结果是概率性的,而不是确切的。
配置方式
方式一:单Redis节点模式
// 默认连接地址 127.0.0.1:6379
RedissonClient redisson = Redisson.create();
Config config = new Config();
config.useSingleServer().setAddress("myredisserver:6379");
RedissonClient redisson = Redisson.create(config);
方式二:spring boot配置
spring:
redis:
host: 127.0.0.1
password: 123456
database: 8
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.12.5</version>
</dependency>
常用数据类型
Map
基于Redis的Redisson的分布式映射结构的RMap Java对象实现 java.util.concurrent.ConcurrentMap接口和java.util.Map接口。与HashMap不同的是,RMap保持了元素的插入顺序。该对象的最大容量受Redis限制,最大元素数量是4 294 967 295个,在特定的场景下,映射缓存(Map)上的高度频繁的读取操作,存在网络通信瓶颈。
Redisson提供了一系列的映射类型的数据结构,这些结构按特性主要分为三大类:
1、元素淘汰(Eviction)类:带有元素淘汰机制的映射类允许针对一个映射中每个元素单独设定 有效时间 和 最长闲置时间,目前的Redis自身并不支持散列(Hash)当中的元素淘汰,因此所有过期元素都是通过org.redisson.EvictionScheduler实例来实现定期清理的,每次运行最多清理300个过期元素。任务的启动时间将根据上次实际清理数量自动调整,间隔时间趋于1秒到1小时之间
2、本地缓存(LocalCache) 类 -- 本地缓存(Local Cache)也叫就近缓存(Near Cache)。这类映射的使用主要用于在特定的场景下,映射缓存(MapCache)上的高度频繁的读取操作,使网络通信都被视为瓶颈的情况。Redisson与Redis通信的同时,还将部分数据保存在本地内存里。这样的设计的好处是它能将读取速度提高最多 45倍 。 所有同名的本地缓存共用一个订阅发布话题,所有更新和过期消息都将通过该话题共享
3、数据分片(Sharding) 类 -- 数据分片(Sharding)类仅适用于Redis集群环境下,因此带有数据分片(Sharding)功能的映射也叫集群分布式映射。它利用分库的原理,将单一一个映射结构切分为若干个小的映射,并均匀的分布在集群中的各个槽里。这样的设计能使一个单一映射结构突破Redis自身的容量限制,让其容量随集群的扩大而增长。在扩容的同时,还能够使读写性能和元素淘汰处理能力随之成线性增长
目录如上,map使用举例:
package com.day.day.up.redission;
import java.util.concurrent.TimeUnit;
import javax.annotation.PostConstruct;
import org.redisson.api.RMapCache;
import org.redisson.api.RedissonClient;
import org.redisson.api.map.event.EntryEvent;
import org.redisson.api.map.event.EntryExpiredListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestController
@RequestMapping("/map")
public class RedissonMapTest {
@Autowired
private RedissonClient redissonClient;
private final static String key="my_test_map";
//初始化Listener,仅初始化一次,过期事件不一定那么及时触发,存在一定的延时
@PostConstruct
public void init(){
RMapCache<String, String> map=redissonClient.getMapCache(key);
// todo: 之前讲到的回调:map里的值过期过期之后,会清理掉,同时会回调此map.addListener
map.addListener(new EntryExpiredListener<String, String>() {
// 此addListener方法最好保证只有一个,否则有几个会调用几个的,可能会导致数据异常
@Override
public void onExpired(EntryEvent<String, String> event) {
log.info("{}已过期,原来的值为:{},现在的值为:{}",event.getKey(),event.getOldValue(),event.getValue());
}
});
log.info("{}初始化完成",key);
}
//存放Key-Value对
@RequestMapping("/put")
public String put(String a,String b,boolean flag){
RMapCache<String, String> map=redissonClient.getMapCache(key);
if(flag) {
map.put(a, b, 10, TimeUnit.SECONDS);
}else{
map.put(a, b);
}
log.info("设置{}={}成功",a,b);
return "SUCCESS";
}
@RequestMapping("/show")
public String put(){
RMapCache<String, String> map=redissonClient.getMapCache(key);
map.keySet().stream().forEach(i->log.info("{},{}",i,map.get(i)));
return "SUCCESS";
}
}
Set
基于Redis的Redisson的分布式Set结构的RSet Java对象实现了java.util.Set接口。通过元素的相互状态比较保证了每个元的唯一性。该对象的最大容量受Redis限制,最大元素数量是4 294 967 295个。
1、集(Set)淘汰机制(Eviction):目前的Redis自身并不支持Set当中的元素淘汰,因此所有过期元素都是通过org.redisson.EvictionScheduler实例来实现定期清理的。为了保证资源的有效利用,每次运行最多清理100个过期元素。任务的启动时间将根据上次实际清理数量自动调整,间隔时间趋于1秒到2小时之间。比如该次清理时删除了100条元素,那么下次执行清理的时间将在1秒以后(最小间隔时间)。一旦该次清理数量少于上次清理数量,时间间隔将增加1.5倍
2、Set数据分片是Redis集群模式下的一个功能
常用使用场景
Map:需要将相似映射数据进行缓存的业务场景,同时可以利用单一元属淘汰机制实现定时触发某些业务逻辑的场景,比如:在库存有限的商品购买业务中,一旦加购成功用户需要在指定时间内支付完成,否则库存自动释放
Set:需要保持数据唯一性或进行排序的业务场景,比如:在用户分享排行榜业务中,为了实时统计每个用户的票数以及当前排名的业务中,就可以使用RScoredSortedSet实现
Set使用代码举例:
package com.day.day.up.redission;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.time.DateUtils;
import org.redisson.api.ExpiredObjectListener;
import org.redisson.api.RScoredSortedSet;
import org.redisson.api.RSet;
import org.redisson.api.RSetCache;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestController
@RequestMapping("/score")
public class RedisRScoredSortedSet {
@Autowired
private RedissonClient redissonClient;
/**
* 添加元素的接口
*/
@RequestMapping("/add")
public String addScore(String a,Double b){
//创建Set
RScoredSortedSet<String> set = redissonClient.getScoredSortedSet("simpleSet1");
//设置过期时间
boolean exists=set.isExists();
set.addListener(new ExpiredObjectListener() {
public void onExpired(String name) {
System.out.println("超时事件被触发,name="+name);
log.info("超时事件被触发,name={}",name);
}
});
//添加元素
set.addScore(a,b);
if(!exists) {
set.expireAt(DateUtils.addMinutes(new Date(), 2));
}
//获取元素在集合中的位置(排序后的位置,例如:粉丝数名次、成绩名次等)
Integer index=set.revRank(a);// revRank方法是反转排序,默认是升序的(分值越低的排名越靠前(例如:第一名是从0开始的))
//获取元素的评分
Double score=set.getScore(a);
log.info("size={},a={},index={},score={}",set.size(),a,index,score);
//可以设置单一元属过期时间,但是不能触发对应过期事件
RSetCache<String> map = redissonClient.getSetCache("simpleSet2");
map.add(a,1, TimeUnit.MINUTES);
map.addListener(new ExpiredObjectListener() {
public void onExpired(String name) {
log.info("entryExpiredListener超时事件被触发,event={}",name);
// 经过测试,此日志是不会打印的,说明这种方式 是不能触发对应过期事件的
}
});
//不能设置单一元素过期
RSet<String> set1 = redissonClient.getSet("simpleSet3");
set1.add(a);
return "SUCCESS";
}
@RequestMapping("/show")
public String showList(String key){
log.info("排行榜={}", key);
RScoredSortedSet<String> set = redissonClient.getScoredSortedSet(key);
set.stream().forEach(a->{
Integer index=set.revRank(a);//获取元素在集合中的位置
Double score=set.getScore(a);//获取元素的评分
log.info("size={},key={},index={},score={}", set.size(), a, index, score);
});
return "SUCCESS";
}
@RequestMapping("/clear")
public String clearList(){
long size=redissonClient.getKeys().deleteByPattern("*impl*");
log.info("删除数量:{}",size);
return "SUCCESS";
}
@RequestMapping("/deleteAll")
public String deleteAll(String pattern){
long amount=redissonClient.getKeys().deleteByPattern(pattern);
log.info("删除数量:{}",amount);
return "SUCCESS";
}
}
List
基于Redis的Redisson分布式列表(List)结构的RList
Java对象在实现了java.util.List接口的同时,确保了元素插入时的顺序。该对象的最大容量受Redis限制,最大元素数量是4 294 967295个
队列
基于Redis的Redisson分布式无界队列(Queue)结构的RQueue
Java对象实现了java.util.Queue接口。尽管RQueue对象无初始大小(边界)限制,但对象的最大容量受Redis限制,最大元素数量是4 294
967 295个
常用队列实现
package com.day.day.up.redission;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import javax.annotation.PostConstruct;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RList;
import org.redisson.api.RQueue;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
//List、Queue测试
@RestController
@RequestMapping("/collection")
public class RedisQueueTest {
@Autowired
private RedissonClient redissonClient;
//List测试 - 添加元素
@RequestMapping("/list/add")
public List<String> addAndGetList(String a){
RList<String> list = redissonClient.getList("my_list");
list.add(a);
return list.readAll();
}
//List测试 - 删除元素
@RequestMapping("/list/del")
public List<String> removeList(String a){
RList<String> list = redissonClient.getList("my_list");
/* 自定义删除条件
list.removeIf(new Predicate<String>() {
@Override
public boolean test(String s) {
return s.length()>10;
}
});*/
//list.remove(a);//删除元素,仅删除匹配到的第一个元素
list.removeAll(Arrays.asList(a));//删除指定集合中所有元素
return list.readAll();
}
//Queue测试 - 添加元素
@RequestMapping("/queue/add")
public List<String> addQueue(String a){
RQueue<String> list = redissonClient.getQueue("my_queue");
list.add(a);//添加一个元素到集合最末尾
return list.readAll();
}
//Queue测试 - 读取元素
@RequestMapping("/queue/poll")
public String pollQueue(){
RQueue<String> list = redissonClient.getQueue("my_queue");
return list.poll();//从队列的头部获取一个元素并从队列中删除该元素,队列为空时返回null
}
//Blocking Queue测试 - 添加元素
@RequestMapping("/blocking/add")
public List<String> addBlockingQueue(String a){
RBlockingQueue<String> list = redissonClient.getBlockingQueue("my_blocking_queue");
list.add(a);
return list.readAll();
}
//Blocking Queue测试 - 读取元素
@RequestMapping("/blocking/get")
public String getBlockingQueue() throws InterruptedException {
RBlockingQueue<String> list = redissonClient.getBlockingQueue("my_blocking_queue");
//return list.poll();//从队列的头部获取一个元素并从队列中删除该元素,队列为空时返回null
return list.take();//从队列的头部获取一个元素并从队列中删除该元素,队列为空时阻塞线程
//return list.peek();//从队列的头部获取一个元素但不删除该元素,队列为空时返回null
}
//Delayed Queue测试 - 添加元素
@RequestMapping("/delayed/add")
public List<String> addDelayedQueue(String a,Long b){
RQueue<String> queue = redissonClient.getQueue("my_blocking_queue");//目标队列
RDelayedQueue<String> list = redissonClient.getDelayedQueue(queue);//延迟队列,数据临时存放地,发出后删除该元素
list.offer(a, b, TimeUnit.SECONDS);
return list.readAll();
}
@PostConstruct
public void acceptElement(){
RBlockingQueue<String> list = redissonClient.getBlockingQueue("my_blocking_queue");
list.subscribeOnElements(new Consumer<String>() {
public void accept(String s) {
System.out.println("获取到元素:"+s);
}
});
}
}
闭锁CountDownLatch
基于Redisson的Redisson分布式闭锁(CountDownLatch)Java对象RCountDownLatch采用了与java.util.concurrent.CountDownLatch相似的接口和用法
package com.day.day.up.redission;
import java.util.concurrent.TimeUnit;
import org.redisson.api.RCountDownLatch;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/count")
public class CountDownLatchTest {
@Autowired
private RedissonClient redissonClient;
//主线程等待所有子线程完成
@RequestMapping("/await")
public void await(){
try {
RCountDownLatch latch = redissonClient.getCountDownLatch("latch");
latch.trySetCount(3);//设置计数器初始大小
latch.await();//阻塞线程直到计数器归零
System.out.println(Thread.currentThread().getName()+"所有子线程已运行完毕");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//子线程
@RequestMapping("/thread")
public void thread(){
try {
RCountDownLatch latch = redissonClient.getCountDownLatch("latch");
System.out.println(Thread.currentThread().getName()+"抵达现场");
TimeUnit.SECONDS.sleep(5);
latch.countDown();//计数器减1,当计数器归零后通知所有等待着的线程恢复执行
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
话题
Redisson的分布式话题,也称为订阅分发,消费者通过订阅指定话题来消费生产者生产的消息数据,使用方式上类似MQ
package com.day.day.up.redission;
import javax.annotation.PostConstruct;
import org.redisson.api.RTopic;
import org.redisson.api.RedissonClient;
import org.redisson.api.listener.MessageListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/*
* 话题(订阅分发)
* */
@RestController
@RequestMapping("/topic")
public class RedisTopicTest {
@Autowired
private RedissonClient redissonClient;
//分发
@RequestMapping("/produce")
public String produce(String a){
RTopic topic = redissonClient.getTopic("anyTopic");
topic.publish(a);
return "发送消息:"+a;
}
//订阅
@PostConstruct
public void consume() {
RTopic topic=redissonClient.getTopic("anyTopic");//订阅指定话题
//RPatternTopic topic=redissonClient.getPatternTopic("*any*");//指定话题表达式订阅多个话题
topic.addListener(String.class, new MessageListener<String>() {
@Override
public void onMessage(CharSequence charSequence, String map) {
System.out.println("接收到消息:"+map);
}
});
}
}
使用场景
List:使用上等同于Java中的List接口
队列:生产者消费者模式的实现、需要异步处理的业务逻辑、有处理顺序要求的需求
闭锁:能够并行处理的业务逻辑,处理完成后在整合数据
话题:使用上等同于日常的MQ消息
限流器
package com.day.day.up.redission;
import org.redisson.api.RRateLimiter;
import org.redisson.api.RateIntervalUnit;
import org.redisson.api.RateType;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/*
* 限流器
* */
@RestController
@RequestMapping("/limiter")
public class RateLimiterTest {
@Autowired
private RedissonClient redissonClient;
//初始化限流器
@RequestMapping("/init")
public void init(){
RRateLimiter limiter=redissonClient.getRateLimiter("rateLimiter");
limiter.trySetRate(RateType.PER_CLIENT,5,1, RateIntervalUnit.SECONDS);//每1秒产生5个令牌
}
//获取令牌
@RequestMapping("/thread")
public void thread(){
RRateLimiter limiter=redissonClient.getRateLimiter("rateLimiter");
if(limiter.tryAcquire()) {//尝试获取1个令牌
System.out.println(Thread.currentThread().getName() + "成功获取到令牌");
}else{
System.out.println(Thread.currentThread().getName() + "未获取到令牌");
}
}
}
事务
package com.day.day.up.redission;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.RandomUtils;
import org.redisson.api.RMap;
import org.redisson.api.RTransaction;
import org.redisson.api.RedissonClient;
import org.redisson.api.TransactionOptions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/*
* Redisson为RMap、RMapCache、RLocalCachedMap、RSet、RSetCache和RBucket这样的对象提供了具有ACID属性的事务功能
* Redisson事务通过分布式锁保证了连续写入的原子性,同时在内部通过操作指令队列实现了Redis原本没有的提交与滚回功能
* 当提交与滚回遇到问题的时候,将通过org.redisson.transaction.TransactionException告知用户
* */
@RestController
@RequestMapping("/tx")
public class RedisTransactionTest {
@Autowired
private RedissonClient redissonClient;
@RequestMapping("/test/{key}")
public void test(@PathVariable String key){
TransactionOptions options=TransactionOptions.defaults().syncSlavesTimeout(5, TimeUnit.SECONDS)
.responseTimeout(3,TimeUnit.SECONDS).retryInterval(2,TimeUnit.SECONDS)
.retryAttempts(3).timeout(5,TimeUnit.SECONDS);
RTransaction transaction=redissonClient.createTransaction(options);
RMap<String, Integer> map=transaction.getMap("myMap");
System.out.println(map.get("userId"));
map.put("userId", RandomUtils.nextInt(1,100));
System.out.println(map.get(key).toString());
try {
transaction.commit();
} catch (Exception e) {
e.printStackTrace();
transaction.rollback();
}
}
}
初步使用--加锁
package com.day.day.up.redission;
import java.util.Calendar;
import java.util.concurrent.TimeUnit;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/lock")
public class RedissonLockTest {
public static int amount=5;
@Autowired
private RedissonClient redissonClient;
//没获取到锁阻塞线程
@RequestMapping(value="/test",method = RequestMethod.GET)
public Integer test(){
RLock lock=null;
try {
lock= redissonClient.getLock("lock");
lock.lock();
System.out.println(formatDate()+" "+Thread.currentThread().getName()+"获取到锁");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (null != lock&&lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
return amount;
}
//立刻返回获取锁的状态
@RequestMapping("/test1")
public Integer test1(){
RLock lock= null;
try {
lock= redissonClient.getLock("lock");
if(lock.tryLock()) {
System.out.println(formatDate() + " " + Thread.currentThread().getName() + "获取到锁");
Thread.sleep(2000);
}else{
System.out.println(formatDate() + " " + Thread.currentThread().getName() + "已抢光");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (null != lock&&lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
return amount;
}
//立刻返回获取锁的状态
@RequestMapping("/test2")
public Integer test2(){
RLock lock= redissonClient.getLock("lock"); //非公平锁,随机取一个等待中的线程分配锁
//RLock lock=redissonClient.getFairLock("lock"); //公平锁,按照先后顺序依次分配锁
try {
if (lock.tryLock(5, 4, TimeUnit.SECONDS)) { //最多等待锁2秒,10秒后强制解锁
System.out.println(formatDate() + " " + Thread.currentThread().getName() + "获取到锁");
Thread.sleep(4500);
}else{
System.out.println(formatDate() + " " + Thread.currentThread().getName() + "未获取到锁");
}
} catch(InterruptedException e){
e.printStackTrace();
} finally{
if (null != lock && lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
return amount;
}
public String formatDate(){
Calendar c=Calendar.getInstance();
return c.get(Calendar.SECOND)+":"+c.get(Calendar.MILLISECOND);
}
}