El entrevistador me preguntó el análisis del principio de concurrencia / subprocesamiento múltiple-CAS de Java, respondí esto y me ofrecí 30k

Tabla de contenido

 

Que es CAS

CAS significa comparar e intercambiar, comparar e intercambiar.

CAS es una operación atómica y CAS utiliza un mecanismo de bloqueo optimista.

Muchas funciones en JUC se basan en CAS. Varias clases atómicas usan CAS para implementar operaciones atómicas en la capa inferior. Se utiliza para resolver el problema de seguridad de la concurrencia.

Además, he recopilado más de 20 años de puntos de conocimiento de entrevistas de empresa, así como varios puntos de conocimiento básicos de Java para compartir con usted de forma gratuita. Si desea información, haga clic (haga clic aquí) para obtenerla gratis .

Problemas de seguridad de simultaneidad

Da un ejemplo típicoi++

public class AddTest {
  public volatile int i;
  public void add() {
    i++;
  }
}

Por javap -c AddTestver las instrucciones de código de bytes, agregue el método:

public void add();
    Code:
       0: aload_0
       1: dup
       2: getfield      #2                  // Field i:I
       5: iconst_1
       6: iadd
       7: putfield      #2                  // Field i:I
      10: return

i++Se divide en varias instrucciones:

  1. Ejecutar getfieldpara obtener el valor de memoria original;
  2. Ejecutar iaddpara agregar 1 operación;
  3. Ejecute la putfieldescritura para volver a escribir el valor acumulado en la memoria.

Suponga una situación:

  • Cuando 线程 1 llega la ejecución iadd, dado que aún no se ha ejecutado putfield, el valor en el área de la memoria principal no se actualizará en este momento.
  • En este momento, 线程 2 entra y comienza a ejecutarse, simplemente copie el valor del área de memoria principal en el área de memoria privada.
  • 线程 1Simplemente ejecute putfieldy actualice el valor del área de memoria principal, luego 线程 2 la copia en este momento es la anterior. Apareció el error.

¿Cómo resolver?

El más simple es agregar sincronizado al método de agregar.

public class AddTest {
  public volatile int i;
  public synchronized void add() {
    i++;
  }
}

Aunque simple y resuelto el problema, el rendimiento no es bueno.

La mejor solución debería ser usar el programa CAS que viene con JDK , como en el ejemplo anterior, use la AtomicIntegerclase

public class AddIntTest {
  public AtomicInteger i;
  public void add() {
    i.getAndIncrement();
  }
}

Principio subyacente

El principio de CAS no es complicado:

  • Tres parámetros, un valor de memoria actual V, valor esperado A, valor actualizado B
  • Si y solo si el valor esperado A y el valor de memoria V son iguales, modifique el valor de memoria a B y devuelva verdadero
  • De lo contrario, no haga nada y devuelva falso

Haga un  AtomicInteger análisis de clase, primero mire el código fuente:

Mi entorno aquí es Java11, si es Java8, algunos nombres internos son ligeramente diferentes.

public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;

    /*
     * This class intended to be implemented using VarHandles, but there
     * are unresolved cyclic startup dependencies.
     */
    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
    private static final long VALUE = U.objectFieldOffset(AtomicInteger.class, "value");

    private volatile int value;
  
		//...
}

Unsafe Clase, esta clase rara vez es útil para el desarrollo general.

Unsafe La capa inferior de la clase está implementada en C / C ++, por lo que todos sus métodos son modificados por la palabra clave nativa.

Puede proporcionar operaciones atómicas a nivel de hardware, como obtener la ubicación de un atributo en la memoria y modificar el valor de campo de un objeto.

punto clave:

  • AtomicInteger El valor almacenado por la clase está en el  value campo y el valuecampo esvolatile

  • En el bloque de código estático, y obtenga la  Unsafe instancia, obtenga el  value desplazamiento del campo en la memoria VALUE

A continuación, de vuelta al ejemplo anterior:

Como se indicó anteriormente, la getAndIncrement() capa inferior del método utiliza tecnología CAS para garantizar la seguridad de la concurrencia.

public final int getAndIncrement() {
  return U.getAndAddInt(this, VALUE, 1);
}

getAndAddInt() método:

public final int getAndAddInt(Object o, long offset, int delta) {
  int v;
  do {
    v = getIntVolatile(o, offset);
  } while (!weakCompareAndSetInt(o, offset, v, v + delta));
  return v;
}

vgetIntVolatile(o, offset)Obtener  por  método, su propósito es obtener   el valor o en el  offsetdesplazamiento, que  o es  AtomicInteger el valor almacenado en la clase, es decir valueel valor del offset desplazamiento de memoria, es decir  VALUE.

Enfoque , weakCompareAndSetInt es realizar el  método central CAS

  • Si la  o suma es  vigual, prueba que ningún otro hilo ha cambiado esta variable, y luego el  v valor se actualiza a  v + delta, donde  delta es el valor incremental actualizado.
  • Por el contrario, CAS continúa funcionando en modo de giro, y este paso también es una operación atómica.

análisis:

  • AtomicInteger El valor original se establece  en A, 线程 1 y 线程 2 cada uno tiene una copia, el valor es A.
  1. 线程 1 Al getIntVolatile(o, offset)obtener el valor A, se 线程 1 suspende en este momento .
  2. 线程 2 El getIntVolatile(o, offset)valor A también se obtiene a través del método, y el weakCompareAndSetIntmétodo se ejecuta para comparar el valor de la memoria como A, y el valor de la memoria se modifica con éxito a B.
  3. En este momento , comparo el método de 线程 1 ejecución weakCompareAndSetInty encuentro que el valor A en mi mano es inconsistente con el valor B de la memoria, lo que indica que el valor ha sido modificado de antemano por otros hilos.
  4. 线程 1 Vuelva a ejecutar getIntVolatile(o, offset)para obtener el valor del valor nuevamente, debido a que el valor de la variable es modificado por volátil y tiene visibilidad, el hilo A continúa ejecutando la weakCompareAndSetIntcomparación y el reemplazo hasta que tiene éxito

CAS necesita atención

Restricciones de uso

CAS es una operación atómica soportada por la CPU. Su atomicidad está garantizada a nivel de hardware. No puede ser usado directamente por usuarios normales en Java, y solo puede ser usado con la ayuda atomicde clases atómicas bajo el paquete, con flexibilidad limitada.

Sin embargo, CAS solo puede garantizar la atomicidad de la operación de una sola variable. Cuando hay múltiples variables involucradas, CAS no puede hacer nada.

La atomicidad no garantiza necesariamente la seguridad de los subprocesos. Por ejemplo, en Java, se necesita volatilecooperación para garantizar la seguridad de los subprocesos.

Problema de ABA

concepto

CAS tiene un problema, un ejemplo es el siguiente:

  • 线程 1 Quite A de la ubicación de memoria V
  • En este momento, A 线程 2 también se toma de la ubicación de memoria V
  • En este momento 线程 1 en el estado suspendido, 线程 2 cambie el valor de la posición V a B, y finalmente a A
  • 线程 1 Vuelva a ejecutar en este momento y encuentre que el valor de la posición V no ha cambiado y continúe ejecutando como se esperaba.

Aunque 线程 1todavía tuvo éxito en este momento , no cumplió con nuestras expectativas reales, y fue equivalente a jugar al 线程 2gato de algalia para el 线程 1príncipe.

Este es el llamado problema ABA

solución

Introducir referencias atómicas, operaciones atómicas con números de versión.

Lleve un número de versión a cada una de nuestras operaciones, para que podamos evitar problemas de ABA. Tanto la idea de bloqueo optimista.

  • Cada vez que cambia el valor en la memoria, se actualiza el número de versión.

  • Al realizar operaciones CAS, al comparar los valores en la memoria, también se comparan los números de versión, sólo cuando los dos no han cambiado puede la ejecución tener éxito.

  • AtomicStampedReferenceLas clases en Java usan números de versión para resolver problemas ABA.

Problemas de costos bajo alta competencia

  • En un entorno altamente competitivo con una alta probabilidad de conflictos simultáneos, si el CAS falla todo el tiempo, siempre volverá a intentarlo y la sobrecarga de la CPU es relativamente alta.

  • Una forma de pensar sobre este problema es introducir un mecanismo de salida, como no salir después de que el número de reintentos supere un cierto umbral.

  • Más importante aún, evite utilizar el bloqueo optimista en un entorno altamente competitivo.

Además, he recopilado más de 20 años de puntos de conocimiento de entrevistas de empresa, así como varios puntos de conocimiento básicos de Java para compartir con usted de forma gratuita. Si desea información, haga clic (haga clic aquí) para obtenerla gratis .

Supongo que te gusta

Origin blog.csdn.net/m0_46757769/article/details/112834716
Recomendado
Clasificación