Descripción general de la clase de contenedor simultáneo de Java

0) Motivo:

   Al principio, en la era de los requisitos de bajo rendimiento del programa, básicamente de programación de un solo subproceso, en ese momento la CPU era básicamente de un solo núcleo. Con el desarrollo de la tecnología de hardware de la computadora, aparecieron las CPU de varios núcleos, y la programación de un solo núcleo en el pasado no ha podido usarla mejor El número de nuestros núcleos de CPU, junto con el aumento de los requisitos de velocidad y rendimiento del programa, ha surgido con la tecnología de programación concurrente. El cuello de botella de rendimiento de los programas de un solo núcleo es el cambio de contexto frecuente, entonces podemos mejorar el rendimiento al reducir el cambio de contexto. Deje que más CPU trabajen juntas de forma asincrónica.

1) Proceso de desarrollo:

        Java es un kit de herramientas de concurrencia agregado en Java5. Este paquete contiene una serie de clases que hacen que la programación concurrente en Java sea cada vez más fácil. Antes de esto, debe implementar manualmente la clase de herramienta relevante. La ruta del paquete se proporciona en forma de una clase de herramienta como java.util.concurrent

        Clases proporcionadas:

    • BloqueoQueue
    • ArrayBlockingQueue
    • DelayQueue
    • LinkedBlockingQueue
    • PriorityBlockingQueue
    • Cola sincrónica
    • Deque de bloqueo
    • Cadena de cola doble bloqueada LinkedBlockingDeque
    • Mapa concurrente Mapa concurrente
    • Mapa simultáneo navegable
    • Block ConutDownLatch
    • Barrera Cíclica
    • Intercambiador
    • Semáforo
    • Servicio de ejecutor
    • ThreadPoolExecutor
    • Servicio de ejecutor programado
    • ForkJoinPool para horquilla y fusión
    • Cerradura
    • ReadWriteLock
    • AtomicBoolean
    • Entero atómico
    • AtomicLong
    • Referencia atómica

      Elegimos los más utilizados para obtener una descripción general.

2) conocimiento teórico necesario

El bloqueo pesimista
siempre supone el peor de los casos. Cada vez que va a obtener los datos, cree que otros lo modificarán, por lo que cada vez que tome los datos, se bloqueará, de modo que otros se bloquearán hasta que obtengan el bloqueo ( Los recursos compartidos solo los usa un subproceso a la vez, otros subprocesos se bloquean y los recursos se transfieren a otros subprocesos después de que se agoten). Muchos de estos mecanismos de bloqueo se utilizan en bases de datos relacionales tradicionales, como bloqueos de fila, bloqueos de tabla, bloqueos de lectura, bloqueos de escritura, etc., que se bloquean antes de realizar operaciones. Los bloqueos exclusivos como sincronizado y ReentrantLock en Java son la realización de ideas de bloqueo pesimistas.

El bloqueo optimista
siempre supone la mejor situación. Cada vez que va a obtener datos, cree que otros no lo modificarán, por lo que no se bloqueará, pero al actualizar, juzgará si otros han actualizado estos datos durante este período. Utilice el mecanismo de número de versión y el algoritmo CAS para lograr. Los bloqueos optimistas son adecuados para los tipos de aplicaciones de lectura múltiple, que pueden mejorar el rendimiento. Al igual que el mecanismo write_condition proporcionado por la base de datos, en realidad son bloqueos optimistas. En Java, la clase de variable atómica bajo el paquete java.util.concurrent.atomic usa CAS, una implementación de bloqueo optimista.

CAS es una tecnología de bloqueo optimista . Cuando varios subprocesos intentan actualizar la misma variable al mismo tiempo usando CAS, solo uno de los subprocesos puede actualizar el valor de la variable, mientras que los otros subprocesos fallan. El subproceso fallido no se suspende, pero se le notifica Falló en esta competencia y puede volver a intentarlo.

3) Los últimos desarrollos:

           La primera clase de colección asociada que aparece en la biblioteca de clases de Java es Hashtableque forma parte de JDK 1.0. HashtableProporciona una función de mapa asociada fácil de usar, segura para subprocesos, que por supuesto es conveniente. Sin embargo, la seguridad de los hilos tiene un precio: Hashtabletodos los métodos están sincronizados. En este momento, la sincronización no competitiva causará un costo de rendimiento considerable. HashtableSucesor HashMapcomo parte de un conjunto de que se produzca JDK 1.2 marco, proporcionando una clase base y una envoltura síncrona sync Collections.synchronizedMapresolver los problemas de seguridad de rosca.

          Pero: El resultado es que aunque en la superficie de estos programas para que funcione correctamente cuando la carga es ligera, pero una vez que la carga es pesada, empezarán a tirar NullPointerExceptionoConcurrentModificationException 异常

随后出现了 ConcurrentMap

ConcurrentMap 的实现

concurrent 包里面就一个类实现了 ConcurrentMap 接口

  • ConcurrentHashMap
ConcurrentHashMap

ConcurrentHashMap 和 HashTable 类很相似,但 ConcurrentHashMap 能提供比 HashTable 更好的并发性能。在你从中读取对象的时候,ConcurrentHashMap 并不会把整个 Map 锁住。此外,在你向其写入对象的时候,ConcurrentHashMap 也不会锁住整个 Map,它的内部只是把 Map 中正在被写入的部分锁定。
其实就是把 synchronized 同步整个方法改为了同步方法里面的部分代码。

另外一个不同点是,在被遍历的时候,即使是 ConcurrentHashMap 被改动,它也不会抛 ConcurrentModificationException。尽管 Iterator 的设计不是为多个线程同时使用。


使用例子:

public class ConcurrentHashMapExample {

    public static void main(String[] args) {
//        HashMap<String, String> map = new HashMap<>();
        ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
        map.put("1", "a");
        map.put("2", "b");
        map.put("3", "c");
        map.put("4", "d");
        map.put("5", "e");
        map.put("6", "f");
        map.put("7", "g");
        map.put("8", "h");
        new Thread1(map).start();
        new Thread2(map).start();

    }

}

class Thread1 extends Thread {

    private final Map map;

    Thread1(Map map) {
        this.map = map;
    }

    @Override
    public void run() {
        super.run();
        try {
            Thread.sleep(20);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        map.remove("6");
    }
}

class Thread2 extends Thread {

    private final Map map;

    Thread2(Map map) {
        this.map = map;
    }

    @Override
    public void run() {
        super.run();
        Set set = map.keySet();
        for (Object next : set) {
            System.out.println(next + ":" + map.get(next));
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}复制代码

CopyOnWriteArrayList

在那些遍历操作大大地多于插入或移除操作的并发应用程序中,一般用 CopyOnWriteArrayList 类替代 ArrayList 。如果是用于存放一个侦听器(listener)列表,例如在AWT或Swing应用程序中,或者在常见的JavaBean中,那么这种情况很常见(相关的 CopyOnWriteArraySet 使用一个 CopyOnWriteArrayList 来实现 Set 接口) 。

原理实现:

如果您正在使用一个普通的 ArrayList 来存放一个侦听器列表,那么只要该列表是可变的,而且可能要被多个线程访问,您 就必须要么在对其进行迭代操作期间,要么在迭代前进行的克隆操作期间,锁定整个列表,这两种做法的开销都很大。当对列表执行会引起列表发生变化的操作时, CopyOnWriteArrayList 并不是为列表创建一个全新的副本,它的迭代器肯定能够返回在迭代器被创建时列表的状态,而不会抛出 ConcurrentModificationException 。在对列表进行迭代之前不必克隆列表或者在迭代期间锁 定列表,因为迭代器所看到的列表的副本是不变的。换句话说, CopyOnWriteArrayList 含有对一个不可变数组的一个可变的引用,因此,只要保留好那个引用,您就可以获得不可变的线程安全性的好处,而且不用锁 定列表。


4)目前最新应用情况:

Tome ConcurrentHashMap como ejemplo:

versión jdk7

ConcurrentHashMap y HashMap tienen ideas de diseño similares, pero con el fin de admitir operaciones concurrentes, se han realizado ciertas mejoras.ConcurrentHashMap presenta el concepto de Segmento, el objetivo es dividir el mapa en múltiples Segmentos (predeterminado 16). La operación ConcurrentHashMap se refina para operar en un determinado segmento. En un entorno de subprocesos múltiples, diferentes subprocesos operan diferentes segmentos, no se afectan entre sí, lo que puede lograr operaciones concurrentes

La última implementación de JDk8:

En comparación con la versión jdk7, ConcurrentHashMap de la versión jdk8 ha enviado muchos cambios. Jdk8 abandonó directamente el diseño de Segment y adoptó un diseño Node + CAS + Synchronized más ligero para garantizar la seguridad del hilo.

807144-6264960638978dff.webp

Diagrama de estructura de ConcurrentHashMap

Mire la estructura general de ConcurrentHashMap arriba, una matriz de nodos, el valor predeterminado es 16, se puede expandir automáticamente, la velocidad de expansión es 0.75

private static finalint DEFAULT_CONCURRENCY_LEVEL = 16;
private static final float LOAD_FACTOR = 0.75f;

Cada nodo monta una lista vinculada. Cuando los datos de montaje de la lista vinculada son mayores que 8, la lista vinculada se convierte automáticamente en un árbol rojo-negro

static final int TREEIFY_THRESHOLD = 8;

Parte del código:

public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
    implements ConcurrentMap<K,V>, Serializable {
    transient volatile Node<K,V>[] table;
}

static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        volatile V val;
        volatile Node<K,V> next;
}

static final class TreeNode<K,V> extends Node<K,V> {
        TreeNode<K,V> parent;  // red-black tree links
        TreeNode<K,V> left;
        TreeNode<K,V> right;
        TreeNode<K,V> prev;    // needed to unlink next upon deletion
        boolean red;
}

static final class TreeBin<K,V> extends Node<K,V> {
        TreeNode<K,V> root;
        volatile TreeNode<K,V> first;
        volatile Thread waiter;
        volatile int lockState;
 }

Supongo que te gusta

Origin www.cnblogs.com/dgwblog/p/12744213.html
Recomendado
Clasificación