ingo@1115: /* ingo@1115: * Copyright (c) 2010 by Intevation GmbH ingo@1115: * ingo@1115: * This program is free software under the LGPL (>=v2.1) ingo@1115: * Read the file LGPL.txt coming with the software for details ingo@1115: * or visit http://www.gnu.org/licenses/ if it does not exist. ingo@1115: */ ingo@1115: 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 :