Java ha diseñado algunas clases para contadores de seguridad numéricos bajo subprocesos múltiples. Estas clases se denominan clases atómicas. Algunas de ellas son las siguientes:
java.util.concurrent.atomic.AtomicBoolean;java.util.concurrent.atomic.AtomicInteger;java.util.concurrent.atomic.AtomicLong;java.util.concurrent.atomic.AtomicReference;
La siguiente es una prueba incremental que compara los valores AtomicInteger y los valores int ordinarios en subprocesos múltiples, utilizando junit4;
Código completo:
paquete test.java;importar java.util.concurrent.CountDownLatch;importar java.util.concurrent.atomic.AtomicInteger;importar org.junit.Assert;importar org.junit.Before;importar org.junit.Test;/** * Pruebe la operación de incremento de AtomicInteger y el valor int ordinario en subprocesos múltiples*/public class TestAtomic { // Objeto de incremento de entero atómico public static AtomicInteger counter_integer; // = new AtomicInteger(0); // Una variable de tipo int public static int count_int = 0; @Before public void setUp() { // Realizar el trabajo de configuración inicial antes de que comiencen todas las pruebas counter_integer = new AtomicInteger(0) ); } @Test public void testAtomic() throws InterruptedException { // Número de subprocesos creados int threadCount = 100; Cuántas veces otros subprocesos afiliados realizan un bucle interno int loopCount = 10000600; // Controla el objeto auxiliar del subproceso afiliado; (otros subprocesos en espera esperan a que se inicie el subproceso principal) CountDownLatch latch_1 = new CountDownLatch(1); objeto del hilo principal Objeto (el hilo principal espera a que todos los hilos subsidiarios terminen de ejecutarse antes de continuar) CountDownLatch latch_n = new CountDownLatch(threadCount); // Crea e inicia otros hilos subsidiarios para (int i =); 0; i < threadCount; i++) { Thread thread = new AtomicIntegerThread(latch_1, latch_n, loopCount); thread.start(); long startNano = System.nanoTime(); ; // Espera a que finalicen otros subprocesos latch_n.await(); // long endNano = System.nanoTime(); counter_integer.get(); // Assert.assertEquals("la suma no es igual a threadCount * loopCount, prueba fallida", suma, threadCount * loopCount System.out.println("--------testAtomic(); ); Se espera que sea igual------------"); System.out.println("Consume tiempo: " + ((endNano - startNano) / (1000 * 1000)) + "ms" ); System.out.println("threadCount = " + (threadCount) + ";"); System.out.println("loopCount = " + (loopCount) + ";"); + (suma) + ";"); } @Test public void testIntAdd() throws InterruptedException { // Número de subprocesos creados int threadCount = 100; Cuántas veces otros subprocesos afiliados realizan un bucle interno int loopCount = 10000600; // Controla el objeto auxiliar del subproceso afiliado; (otros subprocesos en espera esperan a que se inicie el subproceso principal) CountDownLatch latch_1 = new CountDownLatch(1); objeto del hilo principal Objeto (el hilo principal espera a que todos los hilos subsidiarios terminen de ejecutarse antes de continuar) CountDownLatch latch_n = new CountDownLatch(threadCount); // Crea e inicia otros hilos subsidiarios para (int i =); 0; i < threadCount; i++) { Thread thread = new IntegerThread(latch_1, latch_n, loopCount); thread.start(); long startNano = System.nanoTime() // Deja que otros hilos en espera inicien latch_1.countDown de manera uniforme ( ); // Espera a que finalicen otros subprocesos latch_n.await(); // long endNano = System.nanoTime(); Assert.assertNotEquals( "la suma es igual a threadCount * loopCount, la prueba testIntAdd() falló", suma, threadCount * loopCount System.out.println("-------testIntAdd(); Se esperaba que los dos fueran desiguales); - -------"); System.out.println("Consume mucho tiempo: " + ((endNano - startNano) / (1000*1000))+ "ms"); System.out.println("threadCount = " + (threadCount) + ";"); System.out.println("loopCount = " + (loopCount) + ";"); + (suma) + ";"); } // Clase de hilo AtomicIntegerThread extiende Thread { pestillo privado CountDownLatch = nulo; pestillo privado CountDownLatch = nulo; AtomicIntegerThread(CountDownLatch latch, CountDownLatch latchdown, int loopCount) { this.latch = latch; this.latchdown = latchdown; this.loopCount = loopCount } @Override public void run() { // Espere la sincronización de la señal intente { this.latch; await(); } catch (InterruptedException e) { e.printStackTrace() } // para (int i = 0; i <; loopCount; i++) { counter_integer.getAndIncrement(); } // La notificación se reduce en 1 latchdown.countDown(); } } // La clase de hilo IntegerThread extiende el hilo { private CountDownLatch latch = null; ; public IntegerThread(bloqueo CountDownLatch, bloqueo CountDownLatch, int loopCount) { this.latch = pestillo; this.latchdown = latchdown; this.loopCount = loopCount; } @Override public void run() { // Esperar la sincronización de la señal try { this.latch.await() } catch (InterruptedException e) { e.printStackTrace(); } // for (int i = 0; i < loopCount; i++) { count_int++ } // La notificación se reduce en 1 latchdown.countDown();
Los resultados de la ejecución en una PC normal son similares a los siguientes:
---------------testAtomic(); Se esperaba que dos fueran iguales------------------Consumo de tiempo: 85366msthreadCount = 100;loopCount = 10000600;sum = 1000060000;-----------------testIntAdd() no se espera que los dos sean iguales-------------- ----Consume mucho tiempo: 1406msthreadCount = 100;loopCount = 10000600;sum = 119428988;
De esto se puede ver que la diferencia de eficiencia entre la operación AtomicInteger y la operación int es de aproximadamente 50 a 80 veces. Por supuesto, int requiere mucho tiempo. Esta comparación es solo para proporcionar una referencia.
Si se determina que es una ejecución de un solo subproceso, entonces se debe usar int, y la eficiencia de la ejecución de la operación int en subprocesos múltiples es bastante alta, y solo toma 1,5 segundos por mil millones de veces;
(Suponiendo que la CPU es de 2 GHZ, doble núcleo y 4 subprocesos, y el máximo teórico es 8 GHZ, en teoría hay 8 mil millones de ciclos de reloj por segundo.
Mil millones de incrementos int de Java tardan 1,5 segundos, lo que equivale a 12 mil millones de operaciones. Cada ciclo consume 12 ciclos de CPU;
Personalmente, creo que la eficiencia es buena. El lenguaje C también debería requerir más de 4 ciclos de reloj (juicio, ejecución de código interno, juicio de incremento automático, salto).
La premisa es: JVM y CPU no están optimizados radicalmente.
)
De hecho, la eficiencia de AtomicInteger no es baja. Mil millones de veces consumen 80 segundos, y un millón de veces son aproximadamente una milésima de 80 milisegundos.