io.github.zon_g.ABC.client.locks.ReadWriteLock Maven / Gradle / Ivy
/**
* Copyright 2021 the original author, Lin Tang
*
* 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 io.github.zon_g.ABC.client.locks;
import io.github.zon_g.ABC.client.properties.ClientProperties;
import io.github.zon_g.ABC.client.sender.RequestSender;
import io.github.zon_g.ABC.commons.request.LockRequest;
import io.github.zon_g.ABC.commons.request.UnlockRequest;
import io.github.zon_g.ABC.commons.response.Response;
import io.github.zon_g.ABC.commons.types.LockType;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* {@link ReadWriteLock} provides a {@link ReadLock} ( inclusive ) and a {@link WriteLock} ( exclusive ).
*
* Implementation Considerations
*
* {@link ReadLock} of {@link ReadWriteLock} is inclusive, which implies that ReadLock
shared
* the lock, allowing all threads, acquiring the ReadLock
to access the shared resource together
* without modifying shared resource. While {@link WriteLock} of {@link ReadWriteLock} is exclusive. It can
* inferred that only one thread can acquire a WriteLock
at a time. Multiple threads will never
* acquire a WriteLock
until current WriteLock
is released.
*
* Support for Downgrade
*
* {@link ReadWriteLock} supports that a WriteLock
can downgrades to a ReadLock
.
* For example, if current thread acquires a WriteLock
successfully and has not released that
* lock yet, then
*
* - Any other thread, which tries to acquire a
WriteLock
, will fails.
* - Any other thread, which tries to acquire a
ReadLock
, will fails.
*
* But, if current thread tries to acquire a ReadLock
, it will succeeds. At that time, current
* thread holds a WriteLock
and ReadLock
simultaneously. If WriteLock
* is released, then current thread holds only a ReadLock
. Then it can be saying that a
* WriteLock
downgrades to a ReadLock
. For example,
*
*
* {@code
* ReadWriteLock lock = new ReadWriteLock("...");
* ReadWriteLock.ReadLock readLock = lock.readLock();
* ReadWriteLock.WriteLock writeLock = lock.writeLock();
*
* ...
* writeLock.lock();
* ...
* readLock.lock();
* ...
* writeLock.unlock(); // WriteLock downgrades to ReadLock
* ...
* readLock.unlock();
* ...
*
* }
*
*
* No Support for Reentrant Usage
*
* Current {@link ReadWriteLock} does not support for reentrant usage, indicating that if one thread
* acquires a ReadLock
or a WriteLock
successfully, then invocation of
* {@link ReadLock#tryLock()}, {@link ReadLock#lock()}, {@link WriteLock#tryLock()} or {@link WriteLock#lock()}
* fails immediately.
*
* @author Lin Tang
* @see ReadLock
* @see WriteLock
* @since 1.0.0
*/
public class ReadWriteLock extends Lock {
private static final Logger logger = Logger.getLogger(ReadLock.class.getName());
private static final RequestSender sender = RequestSender.getSender();
private static final ClientProperties properties = ClientProperties.getProperties();
ReadWriteLock(String lockKey) {
super(lockKey);
}
public ReadLock readLock() {
return new ReadLock(this.getLockKey());
}
public WriteLock writeLock() {
return new WriteLock(this.getLockKey());
}
@Override
protected boolean tryLock() {
return false;
}
@Override
protected void lock() {
}
@Override
protected void unlock() {
}
@SuppressWarnings("all")
public class ReadLock extends Lock {
ReadLock(String lockKey) {
super(lockKey);
}
@Override
public boolean tryLock() {
if (!this.validateLockKey()) {
if (logger.isLoggable(Level.INFO)) {
logger.log(Level.INFO, EMPTY_LOCK_KEY);
}
return false;
}
if (!this.canLock()) {
if (logger.isLoggable(Level.INFO)) {
logger.log(Level.INFO, LOCKING_ALREADY);
}
return false;
}
LockRequest request = new LockRequest(this.getLockKey(), properties.getApplication(),
Thread.currentThread().getName(), LockType.ReadWriteLock, true, false);
final Response response;
final RequestSender requestSender = sender.clone();
if ((response = requestSender.sendRequest(request)).isSuccess()) {
this.setCanLock(false);
this.setCanUnlock(true);
this.setSuccess(true);
}
return response.isSuccess();
}
@Override
public void lock() {
if (!this.validateLockKey()) {
if (logger.isLoggable(Level.INFO)) {
logger.log(Level.INFO, EMPTY_LOCK_KEY);
}
return;
}
if (!this.canLock()) {
if (logger.isLoggable(Level.INFO)) {
logger.log(Level.INFO, LOCKING_ALREADY);
}
return;
}
LockRequest request = new LockRequest(this.getLockKey(), properties.getApplication(),
Thread.currentThread().getName(), LockType.ReadWriteLock, false, false);
final RequestSender requestSender = sender.clone();
requestSender.sendRequest(request);
this.setCanLock(false);
this.setCanUnlock(true);
this.setSuccess(true);
}
@Override
public void unlock() {
if (!this.isSuccess()) {
if (logger.isLoggable(Level.INFO)) {
logger.log(Level.INFO, LOCKING_FAILS);
}
return;
}
if (!this.canUnlock()) {
if (logger.isLoggable(Level.INFO)) {
logger.log(Level.INFO, UNLOCKING_ALREADY);
}
return;
}
UnlockRequest request = new UnlockRequest(this.getLockKey(), properties.getApplication(),
Thread.currentThread().getName(), LockType.ReadWriteLock, false);
final RequestSender requestSender = sender.clone();
if (requestSender.sendRequest(request).isSuccess()) this.setCanUnlock(false);
}
}
@SuppressWarnings("all")
public class WriteLock extends Lock {
WriteLock(String lockKey) {
super(lockKey);
}
@Override
public boolean tryLock() {
if (!this.validateLockKey()) {
if (logger.isLoggable(Level.INFO)) {
logger.log(Level.INFO, EMPTY_LOCK_KEY);
}
return false;
}
if (!this.canLock()) {
if (logger.isLoggable(Level.INFO)) {
logger.log(Level.INFO, LOCKING_ALREADY);
}
return false;
}
LockRequest request = new LockRequest(this.getLockKey(), properties.getApplication(),
Thread.currentThread().getName(), LockType.ReadWriteLock, true, true);
final Response response;
final RequestSender requestSender = sender.clone();
if ((response = requestSender.sendRequest(request)).isSuccess()) {
this.setCanLock(false);
this.setCanUnlock(true);
this.setSuccess(true);
}
return response.isSuccess();
}
@Override
public void lock() {
if (!this.validateLockKey()) {
if (logger.isLoggable(Level.INFO)) {
logger.log(Level.INFO, EMPTY_LOCK_KEY);
}
return;
}
if (!this.canLock()) {
if (logger.isLoggable(Level.INFO)) {
logger.log(Level.INFO, LOCKING_ALREADY);
}
return;
}
LockRequest request = new LockRequest(this.getLockKey(), properties.getApplication(),
Thread.currentThread().getName(), LockType.ReadWriteLock, false, true);
final RequestSender requestSender = sender.clone();
requestSender.sendRequest(request);
this.setSuccess(true);
this.setCanLock(false);
this.setCanUnlock(true);
}
@Override
public void unlock() {
if (!this.isSuccess()) {
if (logger.isLoggable(Level.INFO)) {
logger.log(Level.INFO, LOCKING_FAILS);
}
return;
}
if (!this.canUnlock()) {
if (logger.isLoggable(Level.INFO)) {
logger.log(Level.INFO, UNLOCKING_ALREADY);
}
return;
}
UnlockRequest request = new UnlockRequest(this.getLockKey(), properties.getApplication(),
Thread.currentThread().getName(), LockType.ReadWriteLock, true);
final RequestSender requestSender = sender.clone();
if (requestSender.sendRequest(request).isSuccess()) this.setCanUnlock(false);
}
}
}