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