2012-07-26 8 views

risposta

17

Poiché AtomicInteger può essere almeno di un ordine di grandezza più lento di un intero protetto da sincronizzato, perché dovrei voler utilizzare AtomicInteger?

AtomicInteger è molto più veloce.

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); 
} 

stampe

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 

aggiungendo qualche contesa come suggerisce @assylias. Mostra che quando si utilizza realmente solo un thread la CPU può ottimizzare l'accesso.

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); 
} 

stampe

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

Ora sono davvero confuso. Questo non è quello che ho capito dalla risposta di @ Gray qui: http://stackoverflow.com/a/11125474/722603 Cosa mi manca? – ef2011

+0

Penso che significhi che 'AtomicInteger' è più lento di un * unsynchronized *' int'; sta parlando del motivo per cui non dovresti sostituire tutti i membri 'int' con' AtomicInteger' senza giustificazione. Ma non è un ordine di grandezza più lento, più di quanto non sia un ordine di grandezza più veloce. Per la maggior parte queste sono tutte piccole differenze di cui stiamo parlando. –

+0

Ricordo di aver letto da qualche parte che il meccanismo di blocco è diverso anche per le cose di Atomic, ma non ho potuto ottenere quel riferimento adesso. Potrei sbagliarmi anch'io. – kosa

2

se si vuole davvero ottenere maggiori dettagli sul motivo per cui java.util.concurrent roba è migliore e quale sia la differenza rispetto al classico approccio sincronizzato, leggere this link (e l'intero blog in generale)