Java a conçu certaines classes pour les compteurs de sécurité numériques sous multi-threading. Ces classes sont appelées classes atomiques. Certaines d'entre elles sont les suivantes :
java.util.concurrent.atomic.AtomicBoolean;java.util.concurrent.atomic.AtomicInteger;java.util.concurrent.atomic.AtomicLong;java.util.concurrent.atomic.AtomicReference;
Ce qui suit est un test incrémentiel comparant les valeurs AtomicInteger et int ordinaires sous multi-threading, en utilisant junit4 ;
Code complet :
package test.java; importer java.util.concurrent.CountDownLatch; importer java.util.concurrent.atomic.AtomicInteger; importer org.junit.Assert; importer org.junit.Before; importer org.junit.Test;/** * Testez l'opération d'incrémentation d'AtomicInteger et la valeur int ordinaire sous multi-threading*/classe publique TestAtomic { // Objet d'incrémentation Atomic Integer public static AtomicInteger counter_integer; // = new AtomicInteger(0); // Une variable de type int public static int count_int = 0; @Before public void setUp() { // Effectuer le travail de configuration initial avant le démarrage de tous les tests counter_integer = new AtomicInteger(0) ); } @Test public void testAtomic() throws InterruptedException { // Nombre de threads créés int threadCount = 100; Combien de fois les autres threads affiliés bouclent en interne int loopCount = 10000600; // Contrôle l'objet auxiliaire du thread affilié ; (les autres threads attendent que le thread principal démarre) CountDownLatch latch_1 = new CountDownLatch(1); objet du thread principal Object ; (le thread principal attend que tous les threads subsidiaires aient fini de s'exécuter avant de continuer) CountDownLatch latch_n = new CountDownLatch(threadCount); // Créer et démarrer d'autres threads subsidiaires pour (int i = 0; i < threadCount; i++) { Thread thread = new AtomicIntegerThread(latch_1, latch_n, loopCount); thread.start(); } long startNano = System.nanoTime(); ; // Attendre que les autres threads terminent latch_n.await(); // long endNano = System.nanoTime(); counter_integer.get(); // Assert.assertEquals("la somme n'est pas égale à threadCount * loopCount, test échoué", sum, threadCount * loopCount System.out.println("--------testAtomic(); ); Devrait être égal------------"); System.out.println("Prend du temps : " + ((endNano - startNano) / (1000 * 1000)) + "ms" ); System.out.println("threadCount = " + (threadCount) + ";"); System.out.println("loopCount = " + (loopCount) + ";"); + (sum) + ";" } @Test public void testIntAdd() throws InterruptedException { // Nombre de threads créés int threadCount = 100; Combien de fois les autres threads affiliés bouclent en interne int loopCount = 10000600; // Contrôle l'objet auxiliaire du thread affilié ; (les autres threads attendent que le thread principal démarre) CountDownLatch latch_1 = new CountDownLatch(1); objet du thread principal Object ; (le thread principal attend que tous les threads subsidiaires aient fini de s'exécuter avant de continuer) CountDownLatch latch_n = new CountDownLatch(threadCount); // Créer et démarrer d'autres threads subsidiaires pour (int i = 0; i < threadCount; i++) { Thread thread = new IntegerThread(latch_1, latch_n, loopCount); thread.start(); } long startNano = System.nanoTime(); ); // Attendez que les autres threads terminent latch_n.await(); // long endNano = System.nanoTime(); Assert.assertNotEquals( "la somme est égale à threadCount * loopCount, le test testIntAdd() a échoué", sum, threadCount * loopCount); System.out.println("-------testIntAdd(); Je m'attendais à ce que les deux soient inégaux- - -------"); System.out.println("Prend du temps : " + ((endNano - startNano) / (1000*1000))+ "ms"); System.out.println("threadCount = " + (threadCount) + ";"); System.out.println("loopCount = " + (loopCount) + ";"); + (somme) + ";" } // Classe de thread AtomicIntegerThread extends { private CountDownLatch latch = null; private CountDownLatch latchdown = null private int loopCount public; AtomicIntegerThread(CountDownLatch latch, CountDownLatch latchdown, int loopCount) { this.latch = latch; this.latchdown = latchdown; this.loopCount = loopCount; } @Override public void run() { // Attendez la synchronisation du signal, essayez { this.latch . wait(); } catch (InterruptedException e) { e.printStackTrace( } // pour (int i = 0; i < loopCount; i++) { counter_integer.getAndIncrement(); } // La notification est décrémentée de 1 latchdown.countDown(); } } // Classe de thread IntegerThread extends { private CountDownLatch latch = null; ; public IntegerThread (CountDownLatch latch, CountDownLatch latchdown, int loopCount) { this.latch = latch; this.latchdown = latchdown; this.loopCount = loopCount; } @Override public void run() { // Attendre la synchronisation du signal try { this.latch.await( } catch (InterruptedException e) { e.printStackTrace(); } // for (int i = 0; i < loopCount; i++) { count_int++; } // La notification est décrémentée de 1 latchdown.countDown();
Les résultats d'exécution sur un PC ordinaire sont similaires aux suivants :
---------------testAtomic(); On s'attend à ce que deux soient égaux------------------Prend du temps : 85366msthreadCount = 100;loopCount = 10000600;sum = 1000060000;-----------------testIntAdd(); Les deux ne devraient pas être égaux-------------- ----Prend du temps : 1406msthreadCount = 100;loopCount = 10000600;sum = 119428988 ;
On peut voir à partir de cela que la différence d'efficacité entre le fonctionnement d'AtomicInteger et le fonctionnement d'int est d'environ 50 à 80 fois. Bien sûr, int prend beaucoup de temps. Cette comparaison est uniquement à titre de référence.
S'il est déterminé qu'il s'agit d'une exécution monothread, alors int doit être utilisé ; et l'efficacité de l'exécution de l'opération int sous multi-thread est assez élevée, et cela ne prend que 1,5 seconde pour 1 milliard de fois ;
(En supposant que le processeur est de 2 GHz, dual-core et 4 threads, et que le maximum théorique est de 8 GHz, il y a théoriquement 8 milliards de cycles d'horloge par seconde.
Un milliard d'incréments Java int prend 1,5 seconde, soit 12 milliards d'opérations Calculé, chaque cycle consomme 12 cycles CPU ;
Personnellement, je pense que l'efficacité est bonne. Le langage C devrait également nécessiter plus de 4 cycles d'horloge (jugement, exécution du code interne, jugement auto-incrémenté, saut)
Le principe est le suivant : la JVM et le CPU ne sont pas radicalement optimisés.
)
En fait, l'efficacité d'AtomicInteger n'est pas faible. Un milliard de fois consomme 80 secondes, et un million de fois équivaut à environ un millième, 80 millisecondes.