view gnv-artifacts/src/main/java/de/intevation/gnv/utils/ExclusiveExec.java @ 820:8288f4a0d7e6

Added more Javadoc. gnv-artifacts/trunk@906 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Tim Englich <tim.englich@intevation.de>
date Mon, 12 Apr 2010 13:23:18 +0000
parents 22c18083225e
children f953c9a559d8
line wrap: on
line source
package de.intevation.gnv.utils;

import java.util.HashMap;

/**
 * This class can be used to synchronize threads with a given key. To use this
 * synchronization, you first need to do call {@link #acquire(java.lang.Object)}
 * to retrieve a {@link UniqueKey}. After this, you can call the code being
 * synchronized. After this execution, you need to call
 * {@link #release(UniqueKey)} with your token you retrieved from {@link
 * #acquire(java.lang.Object)}. A thread needs to wait for another thread if their keys
 * are equal. Threads with different keys don't need to wait for each other.
 *
 * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a>
 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
 */
public final class ExclusiveExec
{
    /**
     * The only instance of this singleton.
     */
    public static final ExclusiveExec INSTANCE = new ExclusiveExec();

    private HashMap tokens;

    /**
     * This class represents a unique key with a reference counter.
     */
    public static class UniqueKey {
        Object key;
        int [] refs;

        /**
         * Constructs a new UniqueKey.
         *
         * @param key The key of this unique key.
         */
        public UniqueKey(Object key) {
            this.key = key;
            refs = new int[1];
        }
    }

    /**
     * Private constructor. Use {@link #INSTANCE} instead.
     */
    private ExclusiveExec() {
        tokens = new HashMap();
    }

    /**
     * This method serves a {@link UniqueKey} and starts a synchronized code
     * block.
     *
     * @param key The key used to identify same threads.
     * @return UniqueKey. Use this object to call {@link #release(UniqueKey)}
     * at the end of your code being synchronized.
     */
    public UniqueKey acquire(Object key) {

        try {
            UniqueKey internalKey = null;
            synchronized (tokens) {
                internalKey = (UniqueKey)tokens.get(key);

                if (internalKey == null) {
                    tokens.put(key, internalKey = new UniqueKey(key));
                }
            }

            synchronized (internalKey) {
                ++internalKey.refs[0];
                while (internalKey.refs[0] > 1) {
                    internalKey.wait(10000L);
                }
            }

            return internalKey;
        }
        catch (InterruptedException ie) {
            return null;
        }
    }

    /**
     * This method releases a lock. Call this method at the end of your code
     * being synchronized.
     *
     * @param internalKey Token retrieved by {@link #acquire(Object)}.
     */
    public void release(UniqueKey internalKey) {
        if (internalKey != null) {
            synchronized (internalKey) {
                if (--internalKey.refs[0] < 1) {
                    synchronized (tokens) {
                        tokens.remove(internalKey.key);
                    }
                }
                internalKey.notifyAll();
            }
        }
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org