Variables atómicas de Java

Resumen

Varios subprocesos que operan en variables compartidas (datos en la memoria de almacenamiento dinámico de Java) traerán errores. Java proporciona un mecanismo de bloqueo (Bloqueo) para administrar la concurrencia de múltiples subprocesos, como sincronizado, pero traerá sobrecarga de rendimiento adicional (bloqueo de subprocesos, cambio de contexto) Etc.). Con el fin de mejorar el rendimiento, Java introdujo variables atómicas para lograr la seguridad de subprocesos múltiples a través de algoritmos sin bloqueo, como CAS.
Las variables atómicas son solo un medio para lograr la seguridad de subprocesos múltiples y son adecuadas para el escenario de operaciones de "lectura-modificación-escritura" en una sola variable compartida, por lo que sus escenarios aplicables no están ampliamente sincronizados.

Problema de subprocesos múltiples

Primero, implemente un contador, el código es el siguiente:

public class Counter {
    private volatile int num;

    public void increment() {
        num++;
    }

    public static void main(String[] args) {
        Counter counter = new Counter();
        // 多线程递增计数器
        IntStream.range(0, 100).parallel().forEach(i -> counter.increment());
        // 打印结果
        System.out.println("counter: " + counter.num);
    }
}

Al ejecutar el código anterior varias veces, el valor impreso no es 100, sino 98, 97, etc.
Este es un problema típico de varios subprocesos. Num ++ parece una simple línea de código, como una operación atómica, pero no es el caso. La operación de incremento puede realizarse en tres pasos:

  1. Leer el valor de la variable num actual
  2. Ejecutar num + 1
  3. Asigne el valor después de +1 a la variable num

Por lo tanto, se sobrescribirá el valor actualizado de varios subprocesos. Por ejemplo, dos subprocesos obtienen el valor de num al mismo tiempo, 50 después de realizar la operación de adición en sus respectivos subprocesos, y luego actualizan el valor en la memoria principal a 51. Pero nuestro valor esperado es 52.

Resuelto por sincronizado

Agregue una palabra clave sincronizada al método increment () de la siguiente manera:

// ...
public synchronized void increment() {
    num++;
}
// ...

Sincronizado es el bloqueo más utilizado en Java, para garantizar que el bloque de código de "supervisión" solo pueda ser ejecutado por un subproceso al mismo tiempo, por lo que el resultado final es 100, correcto.
Sin embargo, este método hará que el subproceso que no ha adquirido el bloqueo se bloquee y se producirá un cambio de contexto. Esta es la sobrecarga de rendimiento causada por bloqueos pesados.

Resuelto por la variable atómica AtomicInteger

La clase AtomicInteger bajo el paquete atómico puede resolver los problemas anteriores, el código es el siguiente:

public class Counter {
    private AtomicInteger num = new AtomicInteger(0);

    public void increment() {
        while (true) {
            int oldValue = num.get();
            int newValue = oldValue + 1;
            if (num.compareAndSet(oldValue, newValue)) {
                return;
            }
        }
    }

    public static void main(String[] args) {
        Counter counter = new Counter();
        // 多线程递增计数器
        IntStream.range(0, 100).parallel().forEach(i -> counter.increment());
        // 打印结果
        System.out.println("counter: " + counter.num);
    }
}

Ejecute el código y la salida 100.

Operación atómica CAS

Las variables atómicas en el paquete concurrente de Java utilizan el mecanismo CAS para implementar operaciones atómicas. La operación atómica mencionada aquí se refiere a la operación atómica (operación de memoria atómica) de la CPU en una memoria determinada, que tiene las siguientes características:

  • Serialice la operación de actualización de varios subprocesos en la misma memoria (para garantizar la seguridad de los datos de actualización de subprocesos múltiples).
  • Las tres operaciones de lectura-modificación-escritura no se pueden interrumpir, o la operación de actualización tendrá éxito o fallará, y no habrá un estado intermedio (para garantizar la integridad de los datos).
  • Solo cuando el valor en la memoria es el mismo que el valor esperado, se realizará la operación de actualización (garantiza la lógica correcta).

En la programación concurrente, CAS pertenece al "bloqueo optimista", suponiendo que la probabilidad de una competencia de subprocesos múltiples es muy pequeña, o que el estado de la competencia terminará en poco tiempo. Si la competencia de subprocesos múltiples es muy frecuente, la CPU estará inactiva durante mucho tiempo (espera ocupada) Causar pérdida de recursos. Entonces, ¡no hay bala de plata! Seleccione la solución técnica de acuerdo con la escena.

CAS (Comparar y cambiar) requiere soporte específico de instrucciones de CPU, por lo que no todas las plataformas de hardware admiten CAS. La naturaleza multiplataforma de Java requiere un comportamiento API consistente, por lo que en plataformas de hardware que no admiten CAS, atomic se degenerará en un bloqueo pesado.

Resumen

Hay muchas maneras de lograr el subprocesamiento múltiple, y elegir una solución técnica razonable de acuerdo con el escenario puede mejorar el rendimiento del programa. Este artículo describe brevemente cómo las variables atómicas en Java resuelven problemas de subprocesos múltiples y algunos conceptos de CAS.

: :
[1] ¿Por qué utilizamos variables atómicas en lugar de una volátil en Java?
[2] Introducción a las variables atómicas en Java
[3] ¿ Cuándo utilizar AtomicReference en Java?
[4] Subprocesos y bloqueos
[5] Comparar e intercambiar
[6] Comprensión y uso de las operaciones de memoria atómica

Supongo que te gusta

Origin www.cnblogs.com/junejs/p/12686921.html
Recomendado
Clasificación