多线程测试redisson实现分布式锁出现org.redisson.RedissonShutdownException: Redisson is shutdown。
原因:多线程还没跑完,主线程就跑完了。主线程走完,关闭了资源。redisson关闭,多线程操作redisson报错:Redisson is shutdown。
解决办法:主线程等待多线程跑完。Thread.sleep(30000);。
1.Junit测试类:RedisDistributedLockTest
package com.user.test.spring_redis; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.user.service.redis.SecondKillService; import com.user.service.redis.SecondKillServiceImp; import com.user.service.redis.SecondKillThread; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration({"classpath:applicationContext.xml"}) public class RedisDistributedLockTest extends AbstractJUnit4SpringContextTests{ @Autowired private SecondKillService secondKillService; @Autowired private SecondKillThread secondKillThread; /** * 模拟秒杀 */ @Test public void secKill(){ System.out.println("秒杀活动开始---"); try { for(int i=0;i<2000;i++){ new Thread(secondKillThread,"Thread" + i).start(); } } catch (Exception e) { e.printStackTrace(); } try { // 主线程需要等待线程执行完,否则,其他线程还没执行完,主线程就走完了,redisson会报错:Redisson is shutdown Thread.sleep(30000); } catch (InterruptedException e1) { e1.printStackTrace(); } System.out.println(SecondKillServiceImp.list); Set set = new HashSet(); for(int i : SecondKillServiceImp.list){ int count = 0; for(int j : SecondKillServiceImp.list){ if(i == j){ count = count + 1; } } if(count > 1){ set.add(i); } } if(set != null && set.size() > 0){ // Iterator it = set.iterator(); // while(it.hasNext()){ // System.out.println(it.next()); // } System.out.println(set); }else{ System.out.println("没有重复的记录!"); } } }
2.线程类:SecondKillThread
package com.user.service.redis; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class SecondKillThread implements Runnable{ @Autowired private SecondKillService secondKillService; @Override public void run() { secondKillService.seckill(); } }
3.秒杀业务接口:SecondKillService
package com.user.service.redis; public interface SecondKillService { public void seckill(); }
4.秒杀业务实现类:SecondKillServiceImp
package com.user.service.redis; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.StringUtils; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.user.base.utils.redis.DistributedLockUtils; import com.user.base.utils.redis.DistributedLockUtils2; import com.user.base.utils.redis.redisson.RedissonConfig; @Service public class SecondKillServiceImp implements SecondKillService{ @Autowired private RedissonClient redissonClient; private static int count = 2000; public static List<Integer> list = new ArrayList<>(); @Override public void seckill() { // count = count - 1; // list.add(count); // System.out.println(Thread.currentThread().getName() + "秒杀操作,singleRedis," + "剩余数量:" + count); // 可以防止重复提交的数据。 String uuid = DistributedLockUtils2.lockWithTimeout("test", 10); // 上锁,如果锁一直保持,其他线程无法操作,只有过期或者主动释放锁。 if(StringUtils.isNotEmpty(uuid)){ try { count = count - 1; list.add(count); System.out.println(Thread.currentThread().getName() + "秒杀操作,singleRedis," + "剩余数量:" + count); } catch (Exception e) { //e.printStackTrace(); } finally { // 如果业务代码出现异常了,不在finally中执行释放锁的操作,也会导致锁无法释放。 DistributedLockUtils2.releaseLock("test",uuid); } }else{ System.out.println("获取锁超时!"); } } // @Override // public void seckill() { // RLock redissonLock = redissonClient.getLock("test"); // // 相当于distributedLockUtil.stringRedisTemplate.opsForValue().setIfAbsent(lockKey, identifier, timeout, TimeUnit.SECONDS) // redissonLock.lock(); // try { // count = count - 1; // list.add(count); // System.out.println(Thread.currentThread().getName() + "秒杀操作,clusterRedis," + "剩余数量:" + count); // } catch (Exception e) { // e.printStackTrace(); // } finally { // // 相当于distributedLockUtil.stringRedisTemplate.delete(lockKey); // /* // * 由于开启了watchdog看门狗线程监听,所以线程执行完之前不会出现:A线程锁过期时间过期,此时B线程设置锁,然后又切换到A线程删锁,误删B线程的锁。 // * 因为A线程执行完之前,A线程的锁会一直续命,不会过期。所以A线程在delete锁之前,会一直持有锁。 // * 如果服务器非宕机情况,那么锁会一直续命,A线程一直持有锁。最终都会执行到finally释放锁。 // * 如果中间出现宕机,那么锁不会续命,到了过期时间就会过期。锁自动释放。 // * 因此不会出现锁无法释放,死锁的情况。 // * // * 自己写续命比较麻烦,而且容易出错。redisson是个很好的框架和解决方案。 // */ // redissonLock.unlock(); // } // } }