variable method java multithreading static synchronization synchronized

method - static class java



Java: subprocesos múltiples que disminuyen la variable estática a través del método sincronizado accediendo a la variable al mismo tiempo (3)

Estoy tratando de sincronizar mis métodos de clase Person para que mi variable de contador estática se reduzca por un hilo en el momento.

public class Person extends Thread { private static int count = 10; public void decrement() { synchronized(Person.class) { count--; } } public int getCount() { return count; } public void run(){ while( count > 0){ this.decrement(); System.out.print(this.getCount() + ","); } } }

Aquí está mi clase principal. Cada subproceso disminuirá al contador estático a través de un método sincronizado para evitar el acceso mutuo de subprocesos al mismo recurso.

public class Main { /** * @param args the command line arguments */ public static void main(String[] args) { Person p1 = new Person(); Person p2 = new Person(); Person p3 = new Person(); Person p4 = new Person(); Person p5 = new Person(); p1.start(); p2.start(); p3.start(); p4.start(); p5.start(); } }

Pero cuando ejecuto mi programa, está imprimiendo valores de contador duplicados. ¿Qué estoy haciendo mal?

Productos:

8,8,7,6,5,4,3,2,1,0 8,7,5,3,1,0,6,8,4,0


La sincronización correcta es la clave, pero usar un AtomicInteger no es la respuesta completa. Lo que necesita saber es que cada subproceso necesita informar sobre el recuento apenas decrementado, que puede cambiarse por otro subproceso incluso si está 1) usando un AtomicInteger o 2) sincronizando correctamente (por separado) los métodos decrement y getCount .

El cuerpo del ciclo while es una sección crítica , una sección de código que no debe interrumpirse.

public void run(){ while( count > 0){ synchronized (Person.class) { decrement(); System.out.print(getCount() + ","); } } }

Salida:

9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4

A veces se detiene a -3 . Ahora, cada instancia es libre de seguir y decrecer, porque comprueba la condición while que pasa, luego se interrumpe el hilo y luego otro hilo disminuye. ¡Entonces el hilo original disminuye a pesar de que ya ha bajado a 0 ! Verifique dentro del bucle.

public void run(){ while( count > 0){ synchronized (Person.class) { if (count > 0) { decrement(); System.out.print(getCount() + ","); } } } }


Lo que está sucediendo en el código original es el siguiente:

  1. Sube una cuenta de decrementos (cuenta -> 9)
  2. Enhebrar dos decrementos cuenta (conteo -> 8)
  3. Subtítulo de tres decrementos (conteo -> 7)
  4. Subtítulo de cuatro decrementos (conteo -> 6)
  5. Enhebrar una cuenta de salidas (recuento: 6)
  6. Enhebrar el conteo de dos salidas (conteo: 6)
  7. Subproceso de tres salidas (conteo: 6)
  8. El hilo cuenta cuatro salidas (cuenta: 6)

Como está bloqueando la disminución y no la disminución y la salida juntas, parece disminuir varias veces.

En otras palabras, no hay garantía de que este código se ejecute de forma consecutiva:

this.decrement(); System.out.print(this.getCount() + ",");

Aquí está el código fijo. Devuelve el valor de recuento actual en decremento para que el nuevo valor pueda devolverse e imprimirse.

public class Person extends Thread { private static int count = 10; public int decrement() { synchronized(Person.class) { count = count - 1; return count; } } public int getCount() { synchronized(Person.class) { return count; } } public void run(){ while( getCount() > 0){ int count = this.decrement(); System.out.println(count); } } }

Sin embargo, recomendaría AtomicInteger para esta tarea:

import java.util.concurrent.atomic.AtomicInteger; public class Person extends Thread { private static AtomicInteger count = new AtomicInteger(10); public int decrement() { return count.decrementAndGet(); } public void run(){ while(count.get() > 0){ int currentCount = this.decrement(); System.out.print(currentCount + ","); } } }


No es suficiente sincronizar solo las escrituras, también debe sincronizar el captador (de lo contrario, el hilo del lector puede leer un valor obsoleto). Pero en este caso, el problema es que otros subprocesos pueden intercalar la ejecución entre el momento en que un subproceso disminuye y el tiempo que el mismo subproceso recupera un valor.

Use un java.util.concurrent.atomic.AtomicInteger para almacenar el conteo. Pero si mantiene métodos separados para la reducción y la obtención (bloqueando el decremento y bloqueando el getter por separado), todavía no hay nada que garantice que los hilos no se entrelacen de forma tal que los duplicados se escriban. El uso del método decrementAndGet de AtomicInteger asegura que el valor disminuido es el que se devuelve.