com.maxifier.mxcache.clean.SuperLock Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mxcache-runtime Show documentation
Show all versions of mxcache-runtime Show documentation
Constains all classes necessary for launching a MxCache-instrumentated application
/*
* Copyright (c) 2008-2014 Maxifier Ltd. All Rights Reserved.
*/
package com.maxifier.mxcache.clean;
import com.maxifier.mxcache.LightweightLock;
import com.maxifier.mxcache.util.TIdentityHashSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.locks.Lock;
/**
* SuperLock is a lock that wraps over few usual locks and locks them simultaneously.
*
* It implements a special locking algorithm that should prevents most common deadlocks, though it
* may lead to starvation.
*
* The algorithm is as follows:
*
* - go sequentially through the underlying locks;
* - when end of array is reached, start from the beginning again;
* - try to acquire current lock with tryLock();
* - if lock is held by another thread:
*
* - release all previously acquired locks
* - acquire current lock with lock();
*
* - if not all locks are acquired yet go to start;
*
*
* @author Alexander Kochurov ([email protected]) (2014-03-31 11:17)
*/
public class SuperLock {
private static final Logger logger = LoggerFactory.getLogger(SuperLock.class);
/** masterLock is used to guard the whole SuperLock */
private final LightweightLock masterLock;
private final Lock[] locks;
/**
* Always holds the number of locked elements. Locks are rearranged in a way that only first n
* elements from locks
are locked.
*/
private int n;
public SuperLock(Collection extends Lock> locks) {
this(locks.toArray(new Lock[locks.size()]));
}
public SuperLock(Lock[] locks) {
this.locks = locks;
masterLock = new LightweightLock();
}
protected void finalize() throws Throwable {
// if there was some problems, notify a user about the problem
if (n != 0) {
logger.error("MxCache hasn't released {} locks: {}", n, Arrays.toString(locks));
}
super.finalize();
}
public void lock() {
masterLock.lock();
int i = 0;
int firstLockedIndex = 0;
int locked = 0;
if (n != 0) {
throw new IllegalMonitorStateException("SuperLock already locked");
}
while (locked < locks.length) {
Lock lock = locks[i];
if (!lock.tryLock()) {
for (; locked > 0; locked--) {
locks[firstLockedIndex++].unlock();
if (firstLockedIndex == locks.length) {
firstLockedIndex = 0;
}
}
lock.lock();
}
locked++;
i++;
if (i == locks.length) {
i = 0;
}
}
n = locks.length;
}
public void unlock() {
if (n > 0) {
for (int i = 0; i < n; i++) {
locks[i].unlock();
}
n = 0;
}
masterLock.unlock();
}
/**
* This method allows to unlocks a part of locks.
*
* @throws IllegalMonitorStateException if this lock hasn't been previously locked with {@link #lock()}
* @throws java.lang.IllegalArgumentException if some of locks passed don't belong to this lock or were already
* unlocked.
*/
public void unlockPartially(TIdentityHashSet locksToRelease) {
if (!masterLock.isHeldByCurrentThread()) {
throw new IllegalMonitorStateException("Lock must be held by current thread");
}
int to = 0;
for (int i = 0; i