All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.redisson.RedissonSpinLock Maven / Gradle / Ivy

Go to download

Easy Redis Java client and Real-Time Data Platform. Valkey compatible. Sync/Async/RxJava3/Reactive API. Client side caching. Over 50 Redis based Java objects and services: JCache API, Apache Tomcat, Hibernate, Spring, Set, Multimap, SortedSet, Map, List, Queue, Deque, Semaphore, Lock, AtomicLong, Map Reduce, Bloom filter, Scheduler, RPC

The newest version!
/**
 * Copyright (c) 2013-2024 Nikita Koksharov
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.redisson;

import org.redisson.api.LockOptions;
import org.redisson.api.RFuture;
import org.redisson.client.codec.LongCodec;
import org.redisson.client.protocol.RedisCommands;
import org.redisson.client.protocol.RedisStrictCommand;
import org.redisson.command.CommandAsyncExecutor;
import org.redisson.misc.CompletableFutureWrapper;

import java.util.Arrays;
import java.util.Collections;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.TimeUnit;

/**
 * Distributed implementation of {@link java.util.concurrent.locks.Lock}
 * Implements reentrant lock.
* Lock will be removed automatically if client disconnects. * This lock implementation doesn't use pub/sub mechanism. It can be used in large Redis clusters despite current naive * pub/sub implementation. *

* Implements a non-fair locking so doesn't guarantees an acquire order. * * @author Danila Varatyntsev */ public class RedissonSpinLock extends RedissonBaseLock { protected long internalLockLeaseTime; protected final LockOptions.BackOff backOff; final CommandAsyncExecutor commandExecutor; public RedissonSpinLock(CommandAsyncExecutor commandExecutor, String name, LockOptions.BackOff backOff) { super(commandExecutor, name); this.commandExecutor = commandExecutor; this.internalLockLeaseTime = getServiceManager().getCfg().getLockWatchdogTimeout(); this.backOff = backOff; } @Override public void lock() { try { lockInterruptibly(-1, null); } catch (InterruptedException e) { throw new IllegalStateException(); } } @Override public void lock(long leaseTime, TimeUnit unit) { try { lockInterruptibly(leaseTime, unit); } catch (InterruptedException e) { throw new IllegalStateException(); } } @Override public void lockInterruptibly() throws InterruptedException { lockInterruptibly(-1, null); } @Override public void lockInterruptibly(long leaseTime, TimeUnit unit) throws InterruptedException { long threadId = Thread.currentThread().getId(); Long ttl = tryAcquire(leaseTime, unit, threadId); // lock acquired if (ttl == null) { return; } LockOptions.BackOffPolicy backOffPolicy = backOff.create(); while (ttl != null) { long nextSleepPeriod = backOffPolicy.getNextSleepPeriod(); Thread.sleep(nextSleepPeriod); ttl = tryAcquire(leaseTime, unit, threadId); } } private Long tryAcquire(long leaseTime, TimeUnit unit, long threadId) { return get(tryAcquireAsync(leaseTime, unit, threadId)); } private RFuture tryAcquireAsync(long leaseTime, TimeUnit unit, long threadId) { if (leaseTime > 0) { RFuture acquiredFuture = tryLockInnerAsync(leaseTime, unit, threadId, RedisCommands.EVAL_LONG); CompletionStage s = handleNoSync(threadId, acquiredFuture); return new CompletableFutureWrapper<>(s); } RFuture ttlRemainingFuture = tryLockInnerAsync(internalLockLeaseTime, TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG); CompletionStage s = handleNoSync(threadId, ttlRemainingFuture); ttlRemainingFuture = new CompletableFutureWrapper<>(s); ttlRemainingFuture.thenAccept(ttlRemaining -> { // lock acquired if (ttlRemaining == null) { scheduleExpirationRenewal(threadId); } }); return ttlRemainingFuture; } @Override public boolean tryLock() { return get(tryLockAsync()); } RFuture tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand command) { internalLockLeaseTime = unit.toMillis(leaseTime); return commandExecutor.syncedEvalNoRetry(getRawName(), LongCodec.INSTANCE, command, "if (redis.call('exists', KEYS[1]) == 0) then " + "redis.call('hincrby', KEYS[1], ARGV[2], 1); " + "redis.call('pexpire', KEYS[1], ARGV[1]); " + "return nil; " + "end; " + "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " + "redis.call('hincrby', KEYS[1], ARGV[2], 1); " + "redis.call('pexpire', KEYS[1], ARGV[1]); " + "return nil; " + "end; " + "return redis.call('pttl', KEYS[1]);", Collections.singletonList(getRawName()), internalLockLeaseTime, getLockName(threadId)); } @Override public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException { final long time = unit.toMillis(waitTime); final long current = System.currentTimeMillis(); final long threadId = Thread.currentThread().getId(); Long ttl = tryAcquire(leaseTime, unit, threadId); // lock acquired if (ttl == null) { return true; } if (System.currentTimeMillis() - current >= time) { acquireFailed(waitTime, unit, threadId); return false; } LockOptions.BackOffPolicy backOffPolicy = backOff.create(); while (true) { Thread.sleep(backOffPolicy.getNextSleepPeriod()); ttl = tryAcquire(leaseTime, unit, threadId); if (ttl == null) { return true; } if (System.currentTimeMillis() - current >= time) { acquireFailed(waitTime, unit, threadId); return false; } } } @Override public boolean tryLock(long waitTime, TimeUnit unit) throws InterruptedException { return tryLock(waitTime, -1, unit); } @Override protected void cancelExpirationRenewal(Long threadId, Boolean unlockResult) { super.cancelExpirationRenewal(threadId, unlockResult); if (unlockResult == null || unlockResult) { internalLockLeaseTime = getServiceManager().getCfg().getLockWatchdogTimeout(); } } @Override public RFuture forceUnlockAsync() { cancelExpirationRenewal(null, null); return commandExecutor.syncedEvalWithRetry(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN, "if (redis.call('del', KEYS[1]) == 1) then " + "return 1 " + "else " + "return 0 " + "end", Collections.singletonList(getRawName())); } protected RFuture unlockInnerAsync(long threadId, String requestId, int timeout) { return evalWriteSyncedNoRetryAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN, "local val = redis.call('get', KEYS[2]); " + "if val ~= false then " + "return tonumber(val);" + "end; " + "if (redis.call('hexists', KEYS[1], ARGV[2]) == 0) then " + "return nil;" + "end; " + "local counter = redis.call('hincrby', KEYS[1], ARGV[2], -1); " + "if (counter > 0) then " + "redis.call('pexpire', KEYS[1], ARGV[1]); " + "redis.call('set', KEYS[2], 0, 'px', ARGV[3]); " + "return 0; " + "else " + "redis.call('del', KEYS[1]); " + "redis.call('set', KEYS[2], 1, 'px', ARGV[3]); " + "return 1; " + "end; ", Arrays.asList(getRawName(), getUnlockLatchName(requestId)), internalLockLeaseTime, getLockName(threadId), timeout); } @Override public RFuture lockAsync(long leaseTime, TimeUnit unit, long currentThreadId) { CompletableFuture result = new CompletableFuture<>(); LockOptions.BackOffPolicy backOffPolicy = backOff.create(); lockAsync(leaseTime, unit, currentThreadId, result, backOffPolicy); return new CompletableFutureWrapper<>(result); } private void lockAsync(long leaseTime, TimeUnit unit, long currentThreadId, CompletableFuture result, LockOptions.BackOffPolicy backOffPolicy) { RFuture ttlFuture = tryAcquireAsync(leaseTime, unit, currentThreadId); ttlFuture.whenComplete((ttl, e) -> { if (e != null) { result.completeExceptionally(e); return; } // lock acquired if (ttl == null) { if (!result.complete(null)) { unlockAsync(currentThreadId); } return; } long nextSleepPeriod = backOffPolicy.getNextSleepPeriod(); getServiceManager().newTimeout( timeout -> lockAsync(leaseTime, unit, currentThreadId, result, backOffPolicy), nextSleepPeriod, TimeUnit.MILLISECONDS); }); } @Override public RFuture tryLockAsync(long threadId) { RFuture longRFuture = tryAcquireAsync(-1, null, threadId); CompletionStage f = longRFuture.thenApply(res -> res == null); return new CompletableFutureWrapper<>(f); } @Override public RFuture tryLockAsync(long waitTime, long leaseTime, TimeUnit unit, long currentThreadId) { CompletableFuture result = new CompletableFuture<>(); LockOptions.BackOffPolicy backOffPolicy = backOff.create(); tryLock(System.currentTimeMillis(), leaseTime, unit, currentThreadId, result, unit.toMillis(waitTime), backOffPolicy); return new CompletableFutureWrapper<>(result); } private void tryLock(long startTime, long leaseTime, TimeUnit unit, long currentThreadId, CompletableFuture result, long waitTime, LockOptions.BackOffPolicy backOffPolicy) { RFuture ttlFuture = tryAcquireAsync(leaseTime, unit, currentThreadId); ttlFuture.whenComplete((ttl, e) -> { if (e != null) { result.completeExceptionally(e); return; } // lock acquired if (ttl == null) { if (!result.complete(true)) { unlockAsync(currentThreadId); } return; } if (System.currentTimeMillis() - startTime >= waitTime) { trySuccessFalse(currentThreadId, result); return; } long nextSleepPeriod = backOffPolicy.getNextSleepPeriod(); getServiceManager().newTimeout( timeout -> tryLock(startTime, leaseTime, unit, currentThreadId, result, waitTime, backOffPolicy), nextSleepPeriod, TimeUnit.MILLISECONDS); }); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy