¿Por HashMap segura hilo?

Introducción: No se recomienda todos sabemos que HashMap no es seguro para subprocesos en un entorno multiproceso, pero se refleja principalmente en lo que se enrosque lugar seguro, este artículo va a descifrar el problema.

El HashMap 1.jdk1.7

En jdk1.8 HashMap hecho un montón de optimización, donde el primer análisis del problema en jdk1.7 en, yo creo que todos sabemos HashMap jdk1.7 propensos bucle infinito en un entorno multiproceso, donde el primero en utilizar el código para simular aparecer muertos situación ciclo:

public class HashMapTest {

    public static void main(String[] args) {
         HashMapThread thread0 = new HashMapThread();
         HashMapThread thread1 = new HashMapThread();
         HashMapThread thread2 = new HashMapThread();
         HashMapThread thread3 = new HashMapThread();
         HashMapThread thread4 = new HashMapThread();
         thread0.start();
         thread1.start();
         thread2.start();
         thread3.start();
         thread4.start();
     }
}

class HashMapThread extends Thread {
    private static AtomicInteger ai = new AtomicInteger();
    private static Map<Integer, Integer> map = new HashMap<>();

    @Override
    public void run() {
        while (ai.get() < 1000000) {
             map.put(ai.get(), ai.get());
             ai.incrementAndGet();
         }
     }
}

El código es relativamente simple, es abrir una pluralidad de hilos continuos operación PUT, y HashMap con AtomicInteger son compartidos a nivel mundial. En los varios tiempos de ejecución más de que aparezca el código de la siguiente escenario bucle infinito:

Hay circunstancias en la que aparecerá una serie de transfronterizas varias veces:

Aquí nos centramos en el análisis de por qué aparece el caso de un bucle infinito, llamado punto de vista de la situación a través del ciclo de muerte y JPS jstack, los resultados son los siguientes:

La información puede ser vista desde el bucle infinito nivel de la pila a través del cual la información puede ser claramente consciente del ciclo de la muerte se produjo en la función de expansión HashMap, la raíz de la función de transferencia en, jdk1.7 en la función de transferencia HashMap es el siguiente:

void transfer(Entry[] newTable, boolean rehash) {
    int newCapacity = newTable.length;
    for (Entry<K,V> e : table) {
        while(null != e) {
            Entry<K,V> next = e.next;
            if (rehash) {
                e.hash = null == e.key ? 0 : hash(e.key);
            }
            int i = indexFor(e.hash, newCapacity);
            e.next = newTable[i];
            newTable[i] = e;
            e = next;
        }
    }
}

Resumir el papel principal de la función:

En la tabla para la expansión de la newtable, la necesidad de transferir datos a la newtable original, líneas Aviso 10-12, donde se puede observar en el curso de los elementos de transferencia, se utiliza el primer método de interpolación, es decir, invirtiendo el orden de la lista se el punto clave aquí es para formar un bucle sin fin. El análisis detallado a continuación.

1.1 expansión resultante en un análisis bucle infinito

Requisitos previos:

Se supone que

# Algoritmo sencillo 1.hash con llave tamaño de la lista mod.

# 2. Inicialmente, el tamaño de la tabla hash = 2, clave = 3,7,5, se encuentran en la tabla [1] en.

# 3. A continuación, cambiar el tamaño, por lo que el tamaño en 4.

Ninguna estructura de datos antes de cambiar el tamaño de la siguiente manera:

Si en el entorno de un solo subproceso, los resultados finales son los siguientes:

proceso de transferencia de aquí, no se describe en detalle, qué hacer una vez que entienda la función de transferencia en el que el proceso de transferencia y la forma de lista invertida no debería ser difícil.

Luego, en un entorno multiproceso, asumen que hay dos hilos A y B durante la operación de venta. Un hilo en la implementación de la función de transferencia de las primeras 11 líneas de código para colgar porque la función en la posición de análisis es muy importante aquí, por lo que ha escrito de nuevo.

Un hilo en este caso los resultados son los siguientes:

Después de que el hilo A suspendido, a continuación, el hilo B ejecuta normalmente, y se completa la operación de cambio de tamaño, los resultados son como sigue:

Se debe prestar especial atención al punto: Debido a que el hilo B se han aplicado, de acuerdo con el modelo de memoria de Java, y ahora la mesa de entrada NEWTABLE en la memoria principal son el valor más reciente: 7.next = 3,3.next = null.

En este momento, el cambio a la rosca A, el valor en la memoria Un hilo suspendido como sigue: e = 3, junto = 7, newtable [3] = null, proceso de ejecución de código es como sigue:

newTable[3]=e ----> newTable[3]=3
e=next ----> e=7

En este caso los siguientes resultados:

Continuar ciclo:

e=7
next=e.next ----> next=3【从主存中取值】
e.next=newTable[3] ----> e.next=3【从主存中取值】
newTable[3]=e ----> newTable[3]=7
e=next ----> e=3

Los resultados son como sigue:

Hacer circular de nuevo:

e=3
next=e.next ----> next=null
e.next=newTable[3] ----> e.next=7 即:3.next=7
newTable[3]=e ----> newTable[3]=3
e=next ----> e=null

Tenga en cuenta que el ciclo: e.next = 7, y en el último ciclo 7.next = 3, se producen lista enlazada circular, y en este momento e = extremos del ciclo nulo.

Los resultados son como sigue:

En cuanto a la estructura de datos del mapa hash de votación, un bucle sin fin ocurrirá aquí en operaciones posteriores, en la tragedia.

1.2 expansión resulta en la pérdida de proceso de análisis de datos

Sigue el análisis anterior, inicialmente:

Enhebrar una y el hilo B es una operación de venta, el mismo hilo A la espera de:

Un hilo conductor en el momento los resultados son como sigue:

En este momento, el hilo B no ha recibido segmentos de tiempo de la CPU, y completar la operación de cambio de tamaño:

También tenga en cuenta que debido a la aplicación de la rosca finalización B, NEWTABLE y la tabla son el valor más reciente: 5.next = null .

En este momento, el cambio a la rosca A, se bloquea el hilo A: E = 7, = 5 Siguiente, newtable = null.. [3.].

Realización newtable [i] = e, ** 7 serán colocados en la mesa de [3] ** la posición donde el próximo = 5. A continuación, el siguiente ciclo:

e=5
next=e.next ----> next=null,从主存中取值
e.next=newTable[1] ----> e.next=5,从主存中取值
newTable[1]=e ----> newTable[1]=5
e=next ----> e=null

5 será colocado en la tabla [1] posición, donde se terminó e = null ciclo, 3 elemento de pérdida , y se forma lista enlazada circular . Y provocar un bucle infinito durante HashMap operaciones posteriores.

2.jdk1.8 中 HashMap

En jdk1.8 a HashMap optimizado, se produce la colisión de hash, ya no se utiliza el primer modo de interpolación, sino directamente en la cola de la lista, por lo que la situación no parece lista enlazada circular, pero todavía inseguro en el caso de multi-hilo aquí vemos la puesta en HashMap fuente jdk1.8 operación:

final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == null) // 如果没有hash碰撞则直接插入元素
        tab[i] = newNode(hash, key, value, null);
    else {
        Node<K,V> e; 
        K k;
    if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
        e = p;
    else if (p instanceof TreeNode)
        e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
    else {
        for (int binCount = 0; ; ++binCount) {
            if ((e = p.next) == null) {
                p.next = newNode(hash, key, value, null);
                if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                    treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
        if (!onlyIfAbsent || oldValue == null)
            e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    ++modCount;
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}

Esta es la función principal de la jdk1.8 HashMap puso en funcionamiento, tenga en cuenta que la línea de código 6, si no hay colisión de hash se produce directamente en el elemento. Si el hilo A y el hilo B se pone operación mientras que sólo los dos datos diferentes como un valor hash, y los datos de posición es nulo, de modo que el hilo A, B entrará en la línea de código 6. Supongamos una situación, se inserta el hilo A en los datos pendientes no se ha realizado, y el hilo B ejecutado normalmente, los datos normales para ser insertados, y el tiempo de CPU hilo adquisición rebanada A, luego enrosque Un hash no más lejos determinación, el problema : enhebrar un hilo B se insertar datos a la cubierta , la ocurrencia de thread-safe.

Aquí es un breve análisis refleja jdk1.8 inferiores aparecen en la inseguridad hilo HashMap, será el seguimiento de las colecciones de Java se resume marco, y luego al análisis específico.

resumen

En primer lugar HashMap es no seguro para subprocesos , lo que refleja principalmente:

# 1. En jdk1.7, en un entorno multiproceso, que se traducirá en una pérdida de datos cadena sin fin o cuando la expansión.

# 2. En jdk1.8, en un entorno multi-hilo, la cobertura de datos del caso tendrá lugar.

Publicado 50 artículos originales · ganado elogios 1706 · Vistas 2,22 millones +

Supongo que te gusta

Origin blog.csdn.net/zl1zl2zl3/article/details/105233273
Recomendado
Clasificación