⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 concurrentreaderhashmap.java

📁 解决如何把XML应用到JAVA里问题
💻 JAVA
📖 第 1 页 / 共 3 页
字号:
/*
 File: ConcurrentReaderHashMap

 Written by Doug Lea. Adapted and released, under explicit
 permission, from JDK1.2 HashMap.java and Hashtable.java which
 carries the following copyright:

 * Copyright 1997 by Sun Microsystems, Inc.,
 * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information
 * of Sun Microsystems, Inc. ("Confidential Information").  You
 * shall not disclose such Confidential Information and shall use
 * it only in accordance with the terms of the license agreement
 * you entered into with Sun.

 History:
 Date       Who                What
 28oct1999  dl               Created
 14dec1999  dl               jmm snapshot
 19apr2000  dl               use barrierLock
 12jan2001  dl               public release
 17nov2001  dl               Minor tunings
 20may2002  dl               BarrierLock can now be serialized.
 09dec2002  dl               Fix interference checks.
 */

package org.dom4j.tree;

import java.io.IOException;
import java.io.Serializable;
import java.util.AbstractCollection;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;

/**
 * A version of Hashtable that supports mostly-concurrent reading, but exclusive
 * writing. Because reads are not limited to periods without writes, a
 * concurrent reader policy is weaker than a classic reader/writer policy, but
 * is generally faster and allows more concurrency. This class is a good choice
 * especially for tables that are mainly created by one thread during the
 * start-up phase of a program, and from then on, are mainly read (with perhaps
 * occasional additions or removals) in many threads. If you also need
 * concurrency among writes, consider instead using ConcurrentHashMap.
 * <p>
 * 
 * Successful retrievals using get(key) and containsKey(key) usually run without
 * locking. Unsuccessful ones (i.e., when the key is not present) do involve
 * brief synchronization (locking). Also, the size and isEmpty methods are
 * always synchronized.
 * 
 * <p>
 * Because retrieval operations can ordinarily overlap with writing operations
 * (i.e., put, remove, and their derivatives), retrievals can only be guaranteed
 * to return the results of the most recently <em>completed</em> operations
 * holding upon their onset. Retrieval operations may or may not return results
 * reflecting in-progress writing operations. However, the retrieval operations
 * do always return consistent results -- either those holding before any single
 * modification or after it, but never a nonsense result. For aggregate
 * operations such as putAll and clear, concurrent reads may reflect insertion
 * or removal of only some entries. In those rare contexts in which you use a
 * hash table to synchronize operations across threads (for example, to prevent
 * reads until after clears), you should either encase operations in
 * synchronized blocks, or instead use java.util.Hashtable.
 * 
 * <p>
 * 
 * This class also supports optional guaranteed exclusive reads, simply by
 * surrounding a call within a synchronized block, as in <br>
 * <code>ConcurrentReaderHashMap t; ... Object v; <br>
 * synchronized(t) { v = t.get(k); } </code><br>
 * 
 * But this is not usually necessary in practice. For example, it is generally
 * inefficient to write:
 * 
 * <pre>
 * 
 *  
 *     ConcurrentReaderHashMap t; ...            // Inefficient version
 *     Object key; ...
 *     Object value; ...
 *     synchronized(t) { 
 *       if (!t.containsKey(key))
 *         t.put(key, value);
 *         // other code if not previously present
 *       }
 *       else {
 *         // other code if it was previously present
 *       }
 *     }
 *  
 *  
 * </pre>
 * 
 * Instead, if the values are intended to be the same in each case, just take
 * advantage of the fact that put returns null if the key was not previously
 * present:
 * 
 * <pre>
 * 
 *  
 *     ConcurrentReaderHashMap t; ...                // Use this instead
 *     Object key; ...
 *     Object value; ...
 *     Object oldValue = t.put(key, value);
 *     if (oldValue == null) {
 *       // other code if not previously present
 *     }
 *     else {
 *       // other code if it was previously present
 *     }
 *  
 *  
 * </pre>
 * 
 * <p>
 * 
 * Iterators and Enumerations (i.e., those returned by keySet().iterator(),
 * entrySet().iterator(), values().iterator(), keys(), and elements()) return
 * elements reflecting the state of the hash table at some point at or since the
 * creation of the iterator/enumeration. They will return at most one instance
 * of each element (via next()/nextElement()), but might or might not reflect
 * puts and removes that have been processed since they were created. They do
 * <em>not</em> throw ConcurrentModificationException. However, these
 * iterators are designed to be used by only one thread at a time. Sharing an
 * iterator across multiple threads may lead to unpredictable results if the
 * table is being concurrently modified. Again, you can ensure interference-free
 * iteration by enclosing the iteration in a synchronized block.
 * <p>
 * 
 * This class may be used as a direct replacement for any use of
 * java.util.Hashtable that does not depend on readers being blocked during
 * updates. Like Hashtable but unlike java.util.HashMap, this class does NOT
 * allow <tt>null</tt> to be used as a key or value. This class is also
 * typically faster than ConcurrentHashMap when there is usually only one thread
 * updating the table, but possibly many retrieving values from it.
 * <p>
 * 
 * Implementation note: A slightly faster implementation of this class will be
 * possible once planned Java Memory Model revisions are in place.
 * 
 * <p>[ <a
 * href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html">
 * Introduction to this package. </a>]
 * 
 */

class ConcurrentReaderHashMap extends AbstractMap implements Map, Cloneable,
        Serializable {

    /*
     * The basic strategy is an optimistic-style scheme based on the guarantee
     * that the hash table and its lists are always kept in a consistent enough
     * state to be read without locking:
     * 
     * Read operations first proceed without locking, by traversing the
     * apparently correct list of the apparently correct bin. If an entry is
     * found, but not invalidated (value field null), it is returned. If not
     * found, operations must recheck (after a memory barrier) to make sure they
     * are using both the right list and the right table (which can change under
     * resizes). If invalidated, reads must acquire main update lock to wait out
     * the update, and then re-traverse.
     * 
     * All list additions are at the front of each bin, making it easy to check
     * changes, and also fast to traverse. Entry next pointers are never
     * assigned. Remove() builds new nodes when necessary to preserve this.
     * 
     * Remove() (also clear()) invalidates removed nodes to alert read
     * operations that they must wait out the full modifications.
     * 
     */

    /** A Serializable class for barrier lock * */
    protected static class BarrierLock implements java.io.Serializable {
    }

    /**
     * Lock used only for its memory effects.
     */
    protected final BarrierLock barrierLock = new BarrierLock();

    /**
     * field written to only to guarantee lock ordering.
     */

    protected transient Object lastWrite;

    /**
     * Force a memory synchronization that will cause all readers to see table.
     * Call only when already holding main synch lock.
     */
    protected final void recordModification(Object x) {
        synchronized (barrierLock) {
            lastWrite = x;
        }
    }

    /**
     * Get ref to table; the reference and the cells it accesses will be at
     * least as fresh as from last use of barrierLock
     */
    protected final Entry[] getTableForReading() {
        synchronized (barrierLock) {
            return table;
        }
    }

    /**
     * The default initial number of table slots for this table (32). Used when
     * not otherwise specified in constructor.
     */
    public static int DEFAULT_INITIAL_CAPACITY = 32;

    /**
     * The minimum capacity, used if a lower value is implicitly specified by
     * either of the constructors with arguments. MUST be a power of two.
     */
    private static final int MINIMUM_CAPACITY = 4;

    /**
     * The maximum capacity, used if a higher value is implicitly specified by
     * either of the constructors with arguments. MUST be a power of two <= 1 <
     * <30.
     */
    private static final int MAXIMUM_CAPACITY = 1 << 30;

    /**
     * The default load factor for this table (1.0). Used when not otherwise
     * specified in constructor.
     */

    public static final float DEFAULT_LOAD_FACTOR = 0.75f;

    /**
     * The hash table data.
     */
    protected transient Entry[] table;

    /**
     * The total number of mappings in the hash table.
     */
    protected transient int count;

    /**
     * The table is rehashed when its size exceeds this threshold. (The value of
     * this field is always (int)(capacity * loadFactor).)
     * 
     * @serial
     */
    protected int threshold;

    /**
     * The load factor for the hash table.
     * 
     * @serial
     */
    protected float loadFactor;

    /**
     * Returns the appropriate capacity (power of two) for the specified initial
     * capacity argument.
     */
    private int p2capacity(int initialCapacity) {
        int cap = initialCapacity;

        // Compute the appropriate capacity
        int result;
        if (cap > MAXIMUM_CAPACITY || cap < 0) {
            result = MAXIMUM_CAPACITY;
        } else {
            result = MINIMUM_CAPACITY;
            while (result < cap)
                result <<= 1;
        }
        return result;
    }

    /**
     * Return hash code for Object x. Since we are using power-of-two tables, it
     * is worth the effort to improve hashcode via the same multiplicative
     * scheme as used in IdentityHashMap.
     */
    private static int hash(Object x) {
        int h = x.hashCode();
        // Multiply by 127 (quickly, via shifts), and mix in some high
        // bits to help guard against bunching of codes that are
        // consecutive or equally spaced.
        return ((h << 7) - h + (h >>> 9) + (h >>> 17));
    }

    /**
     * Check for equality of non-null references x and y.
     */
    protected boolean eq(Object x, Object y) {
        return x == y || x.equals(y);
    }

    /**
     * Constructs a new, empty map with the specified initial capacity and the
     * specified load factor.
     * 
     * @param initialCapacity
     *            the initial capacity The actual initial capacity is rounded to
     *            the nearest power of two.
     * @param loadFactor
     *            the load factor of the ConcurrentReaderHashMap
     * @throws IllegalArgumentException
     *             if the initial maximum number of elements is less than zero,
     *             or if the load factor is nonpositive.
     */

    public ConcurrentReaderHashMap(int initialCapacity, float loadFactor) {
        if (loadFactor <= 0)
            throw new IllegalArgumentException("Illegal Load factor: "
                    + loadFactor);
        this.loadFactor = loadFactor;

        int cap = p2capacity(initialCapacity);

        table = new Entry[cap];
        threshold = (int) (cap * loadFactor);
    }

    /**
     * Constructs a new, empty map with the specified initial capacity and
     * default load factor.
     * 
     * @param initialCapacity
     *            the initial capacity of the ConcurrentReaderHashMap.
     * @throws IllegalArgumentException
     *             if the initial maximum number of elements is less than zero.
     */

    public ConcurrentReaderHashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }

    /**
     * Constructs a new, empty map with a default initial capacity and load
     * factor.
     */

    public ConcurrentReaderHashMap() {
        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
    }

    /**
     * Constructs a new map with the same mappings as the given map. The map is
     * created with a capacity of twice the number of mappings in the given map
     * or 16 (whichever is greater), and a default load factor.
     */

    public ConcurrentReaderHashMap(Map t) {
        this(Math.max((int) (t.size() / DEFAULT_LOAD_FACTOR) + 1, 16),
                DEFAULT_LOAD_FACTOR);
        putAll(t);
    }

    /**
     * Returns the number of key-value mappings in this map.
     * 
     * @return the number of key-value mappings in this map.
     */

    public synchronized int size() {
        return count;
    }

    /**
     * Returns <tt>true</tt> if this map contains no key-value mappings.
     * 
     * @return <tt>true</tt> if this map contains no key-value mappings.
     */

    public synchronized boolean isEmpty() {
        return count == 0;
    }

    /**
     * Returns the value to which the specified key is mapped in this table.
     * 
     * @param key
     *            a key in the table.
     * @return the value to which the key is mapped in this table;
     *         <code>null</code> if the key is not mapped to any value in this
     *         table.
     * @exception NullPointerException
     *                if the key is <code>null</code>.
     * @see #put(Object, Object)
     */

    public Object get(Object key) {

        // throw null pointer exception if key null
        int hash = hash(key);

        /*
         * Start off at the apparently correct bin. If entry is found, we need
         * to check after a barrier anyway. If not found, we need a barrier to
         * check if we are actually in right bin. So either way, we encounter
         * only one barrier unless we need to retry. And we only need to fully
         * synchronize if there have been concurrent modifications.
         */

        Entry[] tab = table;
        int index = hash & (tab.length - 1);
        Entry first = tab[index];
        Entry e = first;

        for (;;) {
            if (e == null) {

                // If key apparently not there, check to
                // make sure this was a valid read

                Entry[] reread = getTableForReading();
                if (tab == reread && first == tab[index])
                    return null;
                else {
                    // Wrong list -- must restart traversal at new first
                    tab = reread;
                    e = first = tab[index = hash & (tab.length - 1)];

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -