多线程测试redisson实现分布式锁出现org.redisson.RedissonShutdownException: Redisson is shutdown

时间:2024-04-16 16:37:11

多线程测试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();
//        }
//    }
}