Java projetou algumas classes para contadores de segurança numéricos em multithreading. Essas classes são chamadas de classes atômicas.
java.util.concurrent.atomic.AtomicBoolean;java.util.concurrent.atomic.AtomicInteger;java.util.concurrent.atomic.AtomicLong;java.util.concurrent.atomic.AtomicReference;
A seguir está um teste incremental comparando AtomicInteger e valores int comuns em multithreading, usando junit4;
Código completo:
pacote 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;/** * Teste a operação de incremento de AtomicInteger e valor int comum em multi-threading*/public class TestAtomic { // Objeto de incremento de número inteiro atômico public static AtomicInteger counter_integer; // = new AtomicInteger(0); // Uma variável do tipo int public static int count_int = 0; @Before public void setUp() { // Execute o trabalho de configuração inicial antes de todos os testes serem iniciados counter_integer = new AtomicInteger(0 ); } @Test public void testAtomic() throws InterruptedException { // Número de threads criados int threadCount = 100; Quantas vezes outros threads afiliados fazem loop internamente int loopCount = 10000600; // Controla o objeto auxiliar do thread afiliado; (outros threads aguardam o início do thread principal) CountDownLatch latch_1 = new CountDownLatch(1); objeto do thread principal Object (o thread principal espera que todos os threads subsidiários terminem a execução antes de continuar) CountDownLatch latch_n = new CountDownLatch(threadCount); 0; i < threadCount; i++) { Thread thread = new AtomicIntegerThread(latch_1, latch_n, loopCount() } long startNano = System.nanoTime(); ; // Aguarde a conclusão de outros threads latch_n.await(); // long endNano = System.nanoTime(); counter_integer.get(); // Assert.assertEquals("soma não é igual a threadCount * loopCount, teste falhou", sum, threadCount * loopCount("--------testAtomic(); ); Espera-se que seja igual -----------"); System.out.println("Demorado: " + ((endNano - startNano) / (1000 * 1000)) + "ms" ); System.out.println("threadCount = " + (threadCount) + ";"); System.out.println("loopCount = " + (loopCount) + ";"); + (soma) + ";"); @Test public void testIntAdd() throws InterruptedException { // Número de threads criados int threadCount = 100; Quantas vezes outros threads afiliados fazem loop internamente int loopCount = 10000600; // Controla o objeto auxiliar do thread afiliado; (outros threads aguardam o início do thread principal) CountDownLatch latch_1 = new CountDownLatch(1); objeto do thread principal Object (o thread principal espera que todos os threads subsidiários terminem a execução antes de continuar) CountDownLatch latch_n = new CountDownLatch(threadCount); 0; i < threadCount; i++) { Thread thread = new IntegerThread(latch_1, latch_n, loopCount); ); // Aguarde a conclusão de outros threads latch_n.await(); // long endNano = System.nanoTime(); Assert.assertNotEquals( "soma é igual a threadCount * loopCount, teste testIntAdd() falhou", sum, threadCount * loopCount("-------testIntAdd(); Esperava que os dois fossem desiguais-); - -------"); System.out.println("Demorado: " + ((endNano - startNano) / (1000*1000))+ "ms"); System.out.println("threadCount = " + (threadCount) + ";"); System.out.println("loopCount = " + (loopCount) + ";"); + (soma) + ";"); // Classe de thread AtomicIntegerThread estende Thread { private CountDownLatch latch = null; private CountDownLatch latchdown = null; AtomicIntegerThread(CountDownLatch latch, CountDownLatch latchdown, int loopCount) { this.latch = latch; this.latchdown = latchdown; this.loopCount = loopCount } @Override public void run() { // Aguarde a sincronização do sinal try { this.latch; .await(); catch (InterruptedException e) { e.printStackTrace(); loopCount; i++) { counter_integer.getAndIncrement() } // A notificação é decrementada em 1 latchdown.countDown(); // Classe de thread IntegerThread estende Thread { private CountDownLatch latch = null; ; public IntegerThread (trava CountDownLatch, trava CountDownLatch, int loopCount) { this.latch = trava; this.latchdown = latchdown; this.loopCount = loopCount; } @Override public void run() { // Aguarde a sincronização do sinal try { this.latch.await() } catch (InterruptedException e) { e.printStackTrace(); } // for (int i = 0; i < loopCount; i++) { count_int++ } // A notificação é decrementada em 1 latchdown.countDown();
Os resultados da execução em um PC comum são semelhantes aos seguintes:
---------------testAtomic(); Espera-se que dois sejam iguais------------------Demorado: 85366msthreadCount = 100;loopCount = 10000600;sum = 1000060000;-----------------testIntAdd(); ---- Demorado: 1406msthreadCount = 100;loopCount = 10000600;sum = 119428988;
Pode-se ver a partir disso que a diferença de eficiência entre a operação AtomicInteger e a operação int é de cerca de 50 a 80 vezes. É claro que int consome muito tempo. Esta comparação serve apenas para fornecer uma referência.
Se for determinado que é uma execução de thread único, então int deve ser usado e a eficiência da execução da operação int em multithreading é bastante alta e leva apenas 1,5 segundos para 1 bilhão de vezes;
(Supondo que a CPU seja de 2 GHz, dual-core e 4 threads, e o máximo teórico seja 8 GHz, existem teoricamente 8 bilhões de ciclos de clock por segundo.
Um bilhão de incrementos Java int leva 1,5 segundos, o que equivale a 12 bilhões de operações. Calculado, cada ciclo consome 12 ciclos de CPU;
Pessoalmente, acho que a eficiência da linguagem C também deve exigir mais de 4 ciclos de clock (julgamento, execução de código interno, julgamento de incremento automático, salto).
A premissa é: JVM e CPU não são radicalmente otimizadas.
)
Na verdade, a eficiência do AtomicInteger não é baixa. Um bilhão de vezes consome 80 segundos e um milhão de vezes equivale a cerca de um milésimo, 80 milissegundos.