ingo@775: package de.intevation.gnv.utils;
ingo@775:
ingo@775: import java.util.HashMap;
ingo@775:
ingo@775: /**
ingo@775: * This class can be used to synchronize threads with a given key. To use this
ingo@815: * synchronization, you first need to do call {@link #acquire(java.lang.Object)}
ingo@815: * to retrieve a {@link UniqueKey}. After this, you can call the code being
sascha@778: * synchronized. After this execution, you need to call
ingo@775: * {@link #release(UniqueKey)} with your token you retrieved from {@link
ingo@815: * #acquire(java.lang.Object)}. A thread needs to wait for another thread if their keys
ingo@775: * are equal. Threads with different keys don't need to wait for each other.
ingo@775: *
sascha@780: * @author Sascha L. Teichmann
sascha@780: * @author Ingo Weinzierl
ingo@775: */
ingo@775: public final class ExclusiveExec
ingo@775: {
ingo@806: /**
ingo@806: * The only instance of this singleton.
ingo@806: */
ingo@775: public static final ExclusiveExec INSTANCE = new ExclusiveExec();
ingo@775:
ingo@775: private HashMap tokens;
ingo@775:
ingo@806: /**
ingo@806: * This class represents a unique key with a reference counter.
ingo@806: */
ingo@775: public static class UniqueKey {
ingo@775: Object key;
ingo@775: int [] refs;
ingo@806:
ingo@806: /**
ingo@806: * Constructs a new UniqueKey.
ingo@806: *
ingo@806: * @param key The key of this unique key.
ingo@806: */
ingo@775: public UniqueKey(Object key) {
ingo@775: this.key = key;
ingo@775: refs = new int[1];
ingo@775: }
ingo@775: }
ingo@775:
ingo@775: /**
ingo@775: * Private constructor. Use {@link #INSTANCE} instead.
ingo@775: */
ingo@775: private ExclusiveExec() {
ingo@775: tokens = new HashMap();
ingo@775: }
ingo@775:
ingo@775: /**
ingo@775: * This method serves a {@link UniqueKey} and starts a synchronized code
ingo@775: * block.
ingo@775: *
ingo@775: * @param key The key used to identify same threads.
ingo@775: * @return UniqueKey. Use this object to call {@link #release(UniqueKey)}
ingo@775: * at the end of your code being synchronized.
ingo@775: */
ingo@775: public UniqueKey acquire(Object key) {
ingo@775:
ingo@775: try {
ingo@775: UniqueKey internalKey = null;
ingo@775: synchronized (tokens) {
ingo@775: internalKey = (UniqueKey)tokens.get(key);
ingo@775:
ingo@775: if (internalKey == null) {
ingo@775: tokens.put(key, internalKey = new UniqueKey(key));
ingo@775: }
ingo@775: }
ingo@775:
ingo@775: synchronized (internalKey) {
ingo@775: ++internalKey.refs[0];
ingo@775: while (internalKey.refs[0] > 1) {
ingo@775: internalKey.wait(10000L);
ingo@775: }
ingo@775: }
ingo@775:
ingo@775: return internalKey;
ingo@775: }
ingo@775: catch (InterruptedException ie) {
ingo@775: return null;
ingo@775: }
ingo@775: }
ingo@775:
ingo@775: /**
ingo@775: * This method releases a lock. Call this method at the end of your code
ingo@775: * being synchronized.
ingo@775: *
ingo@775: * @param internalKey Token retrieved by {@link #acquire(Object)}.
ingo@775: */
ingo@775: public void release(UniqueKey internalKey) {
ingo@775: if (internalKey != null) {
ingo@775: synchronized (internalKey) {
ingo@775: if (--internalKey.refs[0] < 1) {
ingo@775: synchronized (tokens) {
ingo@775: tokens.remove(internalKey.key);
ingo@775: }
ingo@775: }
ingo@775: internalKey.notifyAll();
ingo@775: }
ingo@775: }
ingo@775: }
ingo@775: }
ingo@775: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :