2012-07-08 9 views
9

Sabiendo queUsos de volátiles sin sincronización

lecturas y escrituras son atomic para todas las variables declaradas volátil

Pregunta 1: se puede entender esto como si

private volatile int x = 0; 

x++; la operación es atómica?

Y eso

Marcado variables volátiles no elimina todos necesitan para sincronizar acciones atómicas, porque memory consistency errors are still possible.

Pregunta 2: Me pregunto en qué circunstancias (si lo hay) es posible para ver una variable marcada volatile y no ver ningún método de bloques marcados sincronizados (que intentan acceder/modificar la variable)?

En otras palabras, ¿deben marcarse todas las variables que deben protegerse de la modificación concurrente volatile?

Respuesta

11

Lo volátil solo le ofrece garantías de visibilidad adicionales, escrituras/lecturas atómicas para largos/dobles (de lo contrario, no está garantizado por el JLS, sí) y algunas garantías de órdenes de memoria. No Sincronización (es posible construir bloques de sincronización empezando con solo volátil - Dekker's algorithm) Así que no, no le ayuda con x++ - eso sigue siendo una lectura, inc y escribe y necesita alguna forma de sincronización.

Un ejemplo de volátil es el famoso bloqueo doble comprobación, donde evitamos la sincronización de la mayor parte del tiempo porque las garantías de pedidos son todo lo que necesitamos:

private volatile Helper helper = null; 
public Helper getHelper() { 
    if (helper == null) { 
     synchronized(this) { 
      if (helper == null) { 
       helper = new Helper(); 
      } 
     } 
    } 
    return helper; 
} 

Un ejemplo en el que no hay absolutamente ninguna sincronización involucrados, es un simple indicador de salida, aquí no se trata de garantías que ordenan pero sólo alrededor de la visibilidad garantizada

public volatile boolean exit = false; 
public void run() { 
    while (!exit) doStuff(); 
    // exit when exit set to true 
} 

Si otro hilo establece exit = true el otro hilo haciendo el bucle while está garantizado para ver t él actualiza, sin volátil puede que no.

+0

excelente respuesta y ejemplo. Gracias. – JAM

6

x ++; la operación es atómica?

No. Esto se reduce a x = x + 1. La lectura de x es atómica, y la escritura en x es atómica, pero x = x + 1 como un todo no es atómica.

Me pregunto en qué circunstancias (si lo hay) es posible ver una variable marcada volátil y no ver ningún método de bloques marcados sincronizados (que intentar acceder/modificar la variable)?

Bueno, hay todo tipo de enfoques de concurrencia que no usan synchronized. Hay una gran variedad de otras utilidades de bloqueo en Java, y los algoritmos sin bloqueo que aún requieren cosas como volatile: ConcurrentLinkedQueue son un ejemplo específico, aunque hacen un uso extensivo de atomics "mágicos" compareAndSet.

0

Como un ejemplo rápido comprobable que pueden ilustrar las respuestas anteriores, esto produce siempre un recuento final de 8:

import java.util.concurrent.atomic.AtomicInteger; 


public class ThreadTest_synchronize { 

public static void main(String[] args) { 

    ThreadTest_synchronize tt = new ThreadTest_synchronize(); 
    try { 
     tt.go(); 
    } catch (InterruptedException e) { 
     e.printStackTrace(); 
    } 

} 

private void go() throws InterruptedException{ 

    MyRunnable t = new MyRunnable(); 
    Thread myThread_1 = new Thread(t, "t1"); 
    Thread myThread_2 = new Thread(t, "t2"); 
    myThread_1.start(); 
    myThread_2.start(); 
    myThread_1.join(); 
    myThread_2.join(); 
    System.out.println("Processing count="+t.getCount());  

} 

private class MyRunnable implements Runnable{ 

    private AtomicInteger count=new AtomicInteger(0); 

    @Override 
    public void run() { 
     for(int i=1; i< 5; i++){ 
      doSomething(i); 
      count.getAndAdd(1); 
     }   
    } 


    public AtomicInteger getCount() { 
     return this.count; 
    } 


    private void doSomething(int i) { 
     try { 
      Thread.sleep(i*300); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
    } 
}  

} 

mientras que esto no es así en general:

public class ThreadTest_volatile { 

public static void main(String[] args) { 

    ThreadTest_volatile tt = new ThreadTest_volatile(); 
    try { 
     tt.go(); 
    } catch (InterruptedException e) { 
     e.printStackTrace(); 
    } 

} 

private void go() throws InterruptedException{ 

    MyRunnable t = new MyRunnable(); 
    Thread myThread_1 = new Thread(t, "t1"); 
    Thread myThread_2 = new Thread(t, "t2"); 
    myThread_1.start(); 
    myThread_2.start(); 
    myThread_1.join(); 
    myThread_2.join(); 
    System.out.println("Processing count="+t.getCount());  

} 

private class MyRunnable implements Runnable{ 

    private volatile int count = 0; 


    @Override 
    public void run() { 
     for(int i=1; i< 5; i++){ 
      doSomething(i); 
      count++; 
     } 

    } 

    private int add(int count){ 
     return ++count; 
    } 


    public int getCount(){ 
     return count; 
    } 

    private void doSomething(int i) { 

     try { 
      Thread.sleep(i*300); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
    } 
} 


} 
Cuestiones relacionadas