|
1 | 1 | /*
|
2 |
| - * Copyright 2014-2022 the original author or authors. |
| 2 | + * Copyright 2014-2023 the original author or authors. |
3 | 3 | *
|
4 | 4 | * Licensed under the Apache License, Version 2.0 (the "License");
|
5 | 5 | * you may not use this file except in compliance with the License.
|
|
31 | 31 | import java.util.concurrent.TimeUnit;
|
32 | 32 | import java.util.concurrent.atomic.AtomicBoolean;
|
33 | 33 | import java.util.concurrent.atomic.AtomicInteger;
|
| 34 | +import java.util.concurrent.atomic.AtomicReference; |
34 | 35 | import java.util.concurrent.locks.Lock;
|
35 | 36 | import java.util.stream.Collectors;
|
36 | 37 | import java.util.stream.IntStream;
|
|
59 | 60 | * @author Vedran Pavic
|
60 | 61 | * @author Unseok Kim
|
61 | 62 | * @author Artem Vozhdayenko
|
| 63 | + * @author Anton Gabov |
62 | 64 | *
|
63 | 65 | * @since 4.0
|
64 | 66 | *
|
@@ -811,6 +813,68 @@ void earlyWakeUpTest(RedisLockType testRedisLockType) throws InterruptedExceptio
|
811 | 813 | registry3.destroy();
|
812 | 814 | }
|
813 | 815 |
|
| 816 | + @ParameterizedTest |
| 817 | + @EnumSource(RedisLockType.class) |
| 818 | + void testTwoThreadsRemoveAndObtainSameLockSimultaneously(RedisLockType testRedisLockType) throws Exception { |
| 819 | + final int TEST_CNT = 200; |
| 820 | + final long EXPIRATION_TIME_MILLIS = 10000; |
| 821 | + final long LOCK_WAIT_TIME_MILLIS = 500; |
| 822 | + final String testKey = "testKey"; |
| 823 | + |
| 824 | + final RedisLockRegistry registry = new RedisLockRegistry(redisConnectionFactory, this.registryKey); |
| 825 | + registry.setRedisLockType(testRedisLockType); |
| 826 | + |
| 827 | + for (int i = 0; i < TEST_CNT; i++) { |
| 828 | + final String lockKey = testKey + i; |
| 829 | + final CountDownLatch latch = new CountDownLatch(1); |
| 830 | + final AtomicReference<Lock> lock1 = new AtomicReference<>(); |
| 831 | + final AtomicReference<Lock> lock2 = new AtomicReference<>(); |
| 832 | + |
| 833 | + Thread thread1 = new Thread(() -> { |
| 834 | + try { |
| 835 | + latch.await(); |
| 836 | + // remove lock |
| 837 | + registry.expireUnusedOlderThan(EXPIRATION_TIME_MILLIS); |
| 838 | + // obtain new lock and try to acquire |
| 839 | + Lock lock = registry.obtain(lockKey); |
| 840 | + lock.tryLock(LOCK_WAIT_TIME_MILLIS, TimeUnit.MILLISECONDS); |
| 841 | + lock.unlock(); |
| 842 | + |
| 843 | + lock1.set(lock); |
| 844 | + } |
| 845 | + catch (InterruptedException ignore) { |
| 846 | + } |
| 847 | + }); |
| 848 | + |
| 849 | + Thread thread2 = new Thread(() -> { |
| 850 | + try { |
| 851 | + latch.await(); |
| 852 | + // remove lock |
| 853 | + registry.expireUnusedOlderThan(EXPIRATION_TIME_MILLIS); |
| 854 | + // obtain new lock and try to acquire |
| 855 | + Lock lock = registry.obtain(lockKey); |
| 856 | + lock.tryLock(LOCK_WAIT_TIME_MILLIS, TimeUnit.MILLISECONDS); |
| 857 | + lock.unlock(); |
| 858 | + |
| 859 | + lock2.set(lock); |
| 860 | + } |
| 861 | + catch (InterruptedException ignore) { |
| 862 | + } |
| 863 | + }); |
| 864 | + |
| 865 | + thread1.start(); |
| 866 | + thread2.start(); |
| 867 | + latch.countDown(); |
| 868 | + thread1.join(); |
| 869 | + thread2.join(); |
| 870 | + |
| 871 | + // locks must be the same! |
| 872 | + assertThat(lock1.get()).isEqualTo(lock2.get()); |
| 873 | + } |
| 874 | + |
| 875 | + registry.destroy(); |
| 876 | + } |
| 877 | + |
814 | 878 | private Long getExpire(RedisLockRegistry registry, String lockKey) {
|
815 | 879 | StringRedisTemplate template = createTemplate();
|
816 | 880 | String registryKey = TestUtils.getPropertyValue(registry, "registryKey", String.class);
|
|
0 commit comments