2012-07-26 12 views
13

Dado que AtomicInteger puede tener al menos un orden de magnitud más lento que un int protegido por synchronized, ¿por qué querría utilizar AtomicInteger?¿Cuándo es preferible AtomicInteger en lugar de sincronizado?

Por ejemplo, si todo lo que quiero es incrementar un valor int de una manera segura para los subprocesos, por qué no always uso:

synchronized(threadsafeint) { 
    threadsafeint++; 
} 

lugar de utilizar el mucho más lento AtomicInteger.incrementAndGet()?

Respuesta

17

Dado que AtomicInteger puede ser al menos en un orden de magnitud más lento que un int protegido por sincronización, ¿por qué querría utilizar AtomicInteger?

AtomicInteger es mucho más rápido.

static final Object LOCK1 = new Object(); 
static final Object LOCK2 = new Object(); 
static int i1 = 0; 
static int i2 = 0; 
static final AtomicInteger ai1 = new AtomicInteger(); 
static final AtomicInteger ai2 = new AtomicInteger(); 

public static void main(String... args) throws IOException { 
    for(int i=0;i<5;i++) { 
     testSyncInt(); 
     testAtomicInt(); 
    } 
} 

private static void testSyncInt() { 
    long start = System.nanoTime(); 
    int runs = 10000000; 
    for(int i=0;i< runs;i+=2) { 
     synchronized (LOCK1) { 
      i1++; 
     } 
     synchronized (LOCK2) { 
      i2++; 
     } 
    } 
    long time = System.nanoTime() - start; 
    System.out.printf("sync + incr: Each increment took an average of %.1f ns%n", (double) time/runs); 
} 

private static void testAtomicInt() { 
    long start = System.nanoTime(); 
    int runs = 10000000; 
    for(int i=0;i< runs;i+=2) { 
     ai1.incrementAndGet(); 
     ai2.incrementAndGet(); 
    } 
    long time = System.nanoTime() - start; 
    System.out.printf("incrementAndGet: Each increment took an average of %.1f ns%n", (double) time/runs); 
} 

impresiones

sync + incr: Each increment took an average of 32.4 ns 
incrementAndGet: Each increment took an average of 20.6 ns 
sync + incr: Each increment took an average of 31.4 ns 
incrementAndGet: Each increment took an average of 12.9 ns 
sync + incr: Each increment took an average of 29.6 ns 
incrementAndGet: Each increment took an average of 12.9 ns 
sync + incr: Each increment took an average of 35.1 ns 
incrementAndGet: Each increment took an average of 16.6 ns 
sync + incr: Each increment took an average of 29.9 ns 
incrementAndGet: Each increment took an average of 13.0 ns 

añadiendo un poco de contención como @assylias sugiere. Muestra que cuando solo estás usando un hilo, la CPU puede optimizar el acceso.

static final Object LOCK1 = new Object(); 
static final Object LOCK2 = new Object(); 
static int i1 = 0; 
static int i2 = 0; 
static final AtomicInteger ai1 = new AtomicInteger(); 
static final AtomicInteger ai2 = new AtomicInteger(); 

public static void main(String... args) throws ExecutionException, InterruptedException { 
    for(int i=0;i<5;i++) { 
     testSyncInt(); 
     testAtomicInt(); 
    } 
} 

private static void testSyncInt() throws ExecutionException, InterruptedException { 
    long start = System.nanoTime(); 
    final int runs = 1000000; 
    ExecutorService es = Executors.newFixedThreadPool(2); 
    List<Future<Void>> futures = new ArrayList<>(); 
    for(int t=0;t<8;t++) { 
     futures.add(es.submit(new Callable<Void>() { 
      public Void call() throws Exception { 
       for (int i = 0; i < runs; i += 2) { 
        synchronized (LOCK1) { 
         i1++; 
        } 
        synchronized (LOCK2) { 
         i2++; 
        } 
       } 
       return null; 
      } 
     })); 
    } 
    for (Future<Void> future : futures) { 
     future.get(); 
    } 
    es.shutdown(); 
    long time = System.nanoTime() - start; 
    System.out.printf("sync + incr: Each increment took an average of %.1f ns%n", (double) time/runs/2); 
} 

private static void testAtomicInt() throws ExecutionException, InterruptedException { 
    long start = System.nanoTime(); 
    final int runs = 1000000; 
    ExecutorService es = Executors.newFixedThreadPool(2); 
    List<Future<Void>> futures = new ArrayList<>(); 
    for(int t=0;t<8;t++) { 
     futures.add(es.submit(new Callable<Void>() { 
      public Void call() throws Exception { 
       for (int i = 0; i < runs; i += 2) { 
        ai1.incrementAndGet(); 
        ai2.incrementAndGet(); 
       } 
       return null; 
      } 
     })); 
    } 
    for (Future<Void> future : futures) { 
     future.get(); 
    } 
    es.shutdown(); 
    long time = System.nanoTime() - start; 
    System.out.printf("incrementAndGet: Each increment took an average of %.1f ns%n", (double) time/runs/2); 
} 

impresiones

sync + incr: Each increment took an average of 478.6 ns 
incrementAndGet: Each increment took an average of 191.5 ns 
sync + incr: Each increment took an average of 437.5 ns 
incrementAndGet: Each increment took an average of 169.8 ns 
sync + incr: Each increment took an average of 408.1 ns 
incrementAndGet: Each increment took an average of 180.8 ns 
sync + incr: Each increment took an average of 511.5 ns 
incrementAndGet: Each increment took an average of 313.4 ns 
sync + incr: Each increment took an average of 441.6 ns 
incrementAndGet: Each increment took an average of 219.7 ns 
+0

Ahora estoy realmente confundido. Eso no es lo que entendí de la respuesta de @ Gray aquí: http://stackoverflow.com/a/11125474/722603 ¿Qué me estoy perdiendo? – ef2011

+0

Creo que quiere decir que 'AtomicInteger' es más lento que * unsynchronized *' int'; él está hablando de por qué no debería reemplazar todos sus miembros 'int' con' AtomicInteger' sin justificación. Pero no es un orden de magnitud más lento, como tampoco es un orden de magnitud más rápido. En su mayor parte, todas estas son pequeñas diferencias de las que estamos hablando. –

+0

Recuerdo haber leído en algún lugar que el mecanismo de bloqueo también era diferente para las cosas atómicas, pero no podía obtener esa referencia en este momento. También puedo estar equivocado. – kosa

2

si realmente desea obtener más detalles sobre qué java.util.concurrent cosas es mejor y cuál es la diferencia se compara con la actuación sincronizada clásica, leer this link (y todo el blog en general)

Cuestiones relacionadas