org.jgroups.blocks.locking.LockService Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including
all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and
JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
package org.jgroups.blocks.locking;
import org.jgroups.Event;
import org.jgroups.JChannel;
import org.jgroups.protocols.Locking;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
/**
* LockService is the main class for to use for distributed locking functionality. LockService needs access to a
* {@link JChannel} and interacts with a locking protocol (e.g. {@link org.jgroups.protocols.CENTRAL_LOCK}) via events.
* When no locking protocol is seen on the channel's stack, LockService will throw an exception at startup. An example
* of using LockService is:
*
JChannel ch=new JChannel("/home/bela/locking.xml); // locking.xml needs to have a locking protocol towards the top
LockService lock_service=new LockService(ch);
ch.connect("lock-cluster");
Lock lock=lock_service.getLock("mylock");
lock.lock();
try {
// do something with the lock acquired
}
finally {
lock.unlock();
}
*
*
* The exact semantics of this lock implemantation are defined in {@link LockImpl}.
*
* Note that, contrary to the semantics of {@link java.util.concurrent.locks.Lock}, unlock() can be called multiple
* times; after a lock has been released, future calls to unlock() have no effect.
* @author Bela Ban
* @since 2.12
* @deprecated See http://belaban.blogspot.com/2020/11/i-hate-distributed-locks.html.
*/
@Deprecated
public class LockService {
protected JChannel ch;
protected Locking lock_prot;
public LockService() {
}
public LockService(JChannel ch) {
setChannel(ch);
}
public void setChannel(JChannel ch) {
this.ch=ch;
lock_prot=ch.getProtocolStack().findProtocol(Locking.class);
if(lock_prot == null)
throw new IllegalStateException("Channel configuration must include a locking protocol " +
"(subclass of " + Locking.class.getName() + ")");
}
public Lock getLock(String lock_name) {
return new LockImpl(lock_name);
}
public void unlockAll() {
ch.down(new Event(Event.UNLOCK_ALL));
}
public void unlockForce(String lock_name) {
ch.down(new Event(Event.UNLOCK_FORCE, lock_name));
}
public void addLockListener(LockNotification listener) {
lock_prot.addLockListener(listener);
}
public void removeLockListener(LockNotification listener) {
lock_prot.removeLockListener(listener);
}
public String printLocks() {
return lock_prot.printLocks();
}
/**
* Implementation of {@link Lock}. This is a client stub communicates with a server equivalent. The semantics are
* more or less those of {@link Lock}, but may differ slightly.
* There is no reference counting of lock owners, so acquisition of a lock already held by a thread is a no-op.
* Also, releasing the lock after it was already released is a no-op as well.
*
* An exact description is provided below.
*/
protected class LockImpl implements Lock {
protected final String name;
protected final AtomicReference holder=new AtomicReference<>();
public LockImpl(String name) {
this.name=name;
}
/**
* {@inheritDoc}
* Blocks until the lock has been acquired. Masks interrupts; if an interrupt was received on entry or while
* waiting for the lock acquisition, it won't cause the call to return. However, the thread's status will be
* set to interrupted when the call returns.
*/
@Override
public void lock() {
ch.down(new Event(Event.LOCK, new LockInfo(name, false, false, false, 0, TimeUnit.MILLISECONDS)));
holder.set(Thread.currentThread());
}
/**
* {@inheritDoc}
* If the thread is interrupted at entry, the call will throw an InterruptedException immediately and the lock
* won't be acquired. If the thread is interrupted while waiting for the lock acquition, an InterruptedException
* will also be thrown immediately. The thread's interrupt status will not be set after the call returns.
* @throws InterruptedException
*/
public void lockInterruptibly() throws InterruptedException {
ch.down(new Event(Event.LOCK, new LockInfo(name, false, true, false, 0, TimeUnit.MILLISECONDS)));
Thread currentThread = Thread.currentThread();
if(currentThread.isInterrupted())
throw new InterruptedException();
else
holder.set(currentThread);
}
/**
* {@inheritDoc}
* If the thread is interrupted at entry or during the call, no InterruptedException will be thrown, but the
* thread's status will be set to interrupted upon return. An interrupt has therefore no impact on the
* return value (success or failure).
*/
public boolean tryLock() {
Boolean retval=(Boolean)ch.down(new Event(Event.LOCK, new LockInfo(name, true, false, false, 0, TimeUnit.MILLISECONDS)));
if(retval != null && retval)
holder.set(Thread.currentThread());
return retval == null ? false : retval;
}
/**
* {@inheritDoc}
* * If the thread is interrupted at entry, the call will throw an InterruptedException immediately and the lock
* won't be acquired. If the thread is interrupted while waiting for the lock acquition, an InterruptedException
* will also be thrown immediately. The thread's interrupt status will not be set after the call returns.
* @param time
* @param unit
* @return
* @throws InterruptedException
*/
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
Boolean retval=(Boolean)ch.down(new Event(Event.LOCK, new LockInfo(name, true, true, true, time, unit)));
if(Thread.currentThread().isInterrupted())
throw new InterruptedException();
if(retval != null && retval)
holder.set(Thread.currentThread());
return retval == null ? false : retval;
}
/**
* {@inheritDoc}
* Releases a lock. Contrary to the parent's implementation, this method can be called more than once:
* the release of a lock that has already been released, or is not owned by this thread is a no-op.
*/
public void unlock() {
ch.down(new Event(Event.UNLOCK, new LockInfo(name, false, false, false, 0, TimeUnit.MILLISECONDS)));
holder.compareAndSet(Thread.currentThread(), null); // only set if the current thread is actually the holder
}
/**
* This condition object is only allowed to work 1 for each lock.
* If more than 1 condition is created for this lock, they both will
* be awaiting/signalling on the same lock
*/
public Condition newCondition() {
return new ConditionImpl(name, holder);
}
public String toString() {
return name + (holder.get() == null? " [unlocked]" : " [held by " + holder.get() + "]");
}
}
private class ConditionImpl implements Condition {
protected final String name;
protected final AtomicReference holder;
public ConditionImpl(String name, AtomicReference holder) {
this.name=name;
this.holder=holder;
}
@Override
public void await() throws InterruptedException {
ch.down(new Event(Event.LOCK_AWAIT, new LockInfo(name, false,
true, false, 0, TimeUnit.MILLISECONDS)));
if(Thread.currentThread().isInterrupted())
throw new InterruptedException();
}
@Override
public void awaitUninterruptibly() {
ch.down(new Event(Event.LOCK_AWAIT, new LockInfo(name, false,
false, false, 0, TimeUnit.MILLISECONDS)));
}
@Override
public long awaitNanos(long nanosTimeout) throws InterruptedException {
Long waitLeft = (Long)ch.down(new Event(Event.LOCK_AWAIT,
new LockInfo(name, false, true, true, nanosTimeout,
TimeUnit.NANOSECONDS)));
if(Thread.currentThread().isInterrupted())
throw new InterruptedException();
return waitLeft;
}
@Override
public boolean await(long time, TimeUnit unit)
throws InterruptedException {
return awaitNanos(unit.toNanos(time)) > 0;
}
@Override
public boolean awaitUntil(Date deadline) throws InterruptedException {
long waitUntilTime = deadline.getTime();
long currentTime = System.currentTimeMillis();
long waitTime = waitUntilTime - currentTime;
return waitTime > 0 && await(waitTime, TimeUnit.MILLISECONDS);
}
@Override
public void signal() {
if (holder.get() != Thread.currentThread()) {
throw new IllegalMonitorStateException();
}
ch.down(new Event(Event.LOCK_SIGNAL, new AwaitInfo(name, false)));
}
@Override
public void signalAll() {
if (holder.get() != Thread.currentThread()) {
throw new IllegalMonitorStateException();
}
ch.down(new Event(Event.LOCK_SIGNAL, new AwaitInfo(name, true)));
}
}
}