多線程測試redisson實現分布式鎖出現org.redisson.RedissonShutdownException: Redisson is shutdown


多線程測試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();
//        }
//    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM