Java는 멀티스레딩에서 수치 안전 카운터를 위한 일부 클래스를 설계했습니다. 이러한 클래스를 원자 클래스라고 합니다.
java.util.concurrent.atomic.AtomicBoolean;java.util.concurrent.atomic.AtomicInteger;java.util.concurrent.atomic.AtomicLong;java.util.concurrent.atomic.AtomicReference;
다음은 junit4를 사용하여 멀티스레딩 하에서 AtomicInteger와 일반 int 값을 비교하는 증분 테스트이다.
전체 코드:
패키지 test.java;import java.util.concurrent.CountDownLatch;import java.util.concurrent.atomic.AtomicInteger;import org.junit.Assert;import org.junit.Before;import org.junit.Test;/** * 멀티스레딩*/public class TestAtomic { // Atomic Integer 증가 객체 public static에서 AtomicInteger 및 일반 int 값의 증가 연산을 테스트합니다. AtomicInteger counter_integer; // = new AtomicInteger(0); // int 유형의 변수 public static int count_int = 0; @Before public void setUp() { // 모든 테스트가 시작되기 전에 초기 설정 작업을 수행합니다. counter_integer = new AtomicInteger(0 ); } @Test public void testAtomic() throws InterruptedException { // 생성된 스레드 수 int threadCount = 100; 다른 관련 스레드가 내부적으로 루프하는 횟수 int loopCount = 10000600; // 관련 스레드의 보조 개체를 제어합니다. (다른 대기 스레드는 기본 스레드가 시작될 때까지 기다립니다.) CountDownLatchatch_1 = new CountDownLatch(1); 메인 스레드의 객체 Object; (메인 스레드는 계속하기 전에 모든 보조 스레드의 실행이 완료될 때까지 기다립니다) CountDownLatch 래치_n = new CountDownLatch(threadCount) // 다른 보조 스레드 생성 및 시작 for (int i = 0; i < threadCount; i++) { Thread thread = new AtomicIntegerThread(latch_1,atch_n, loopCount); thread.start() } long startNano = System.nanoTime(); ; // 다른 스레드가 완료될 때까지 기다립니다.atch_n.await(); // long endNano = System.nanoTime(); counter_integer.get(); // Assert.assertEquals("sum이 threadCount * loopCount와 같지 않음, 테스트 실패", sum, threadCount * loopCount) System.out.println("---------testAtomic( ); 동일할 것으로 예상됨------------"); System.out.println("시간 소모: " + ((endNano - startNano) / (1000 * 1000)) + "ms" ); System.out.println("threadCount = " + (threadCount) + ";"); System.out.println("loopCount = " + (loopCount) + ";") System.out.println("sum = " + (sum) + ";") } @Test public void testIntAdd() throws InterruptedException { // 생성된 스레드 수 int threadCount = 100; 다른 관련 스레드가 내부적으로 루프하는 횟수 int loopCount = 10000600; // 관련 스레드의 보조 개체를 제어합니다. (다른 대기 스레드는 기본 스레드가 시작될 때까지 기다립니다.) CountDownLatchatch_1 = new CountDownLatch(1); 메인 스레드의 객체 Object; (메인 스레드는 계속하기 전에 모든 보조 스레드의 실행이 완료될 때까지 기다립니다) CountDownLatch 래치_n = new CountDownLatch(threadCount) // 다른 보조 스레드 생성 및 시작 for (int i = 0; i < threadCount; i++) { Thread thread = new IntegerThread(latch_1,atch_n, loopCount); thread.start() } long startNano = System.nanoTime(); ); // 다른 스레드가 완료될 때까지 기다립니다. // long endNano = System.nanoTime(); Assert.assertNotEquals( "sum은 threadCount * loopCount, testIntAdd() 테스트 실패", sum, threadCount * loopCount) System.out.println("------testIntAdd(); 두 개가 동일하지 않을 것으로 예상됩니다. - -------"); System.out.println("시간 소모: " + ((endNano - startNano) / (1000*1000))+ "ms"); System.out.println("threadCount = " + (threadCount) + ";"); System.out.println("loopCount = " + (loopCount) + ";") System.out.println("sum = " + (sum) + ";"); } // 스레드 클래스 AtomicIntegerThread는 Thread { private CountDownLatch 래치 = null; AtomicIntegerThread(CountDownLatch 래치, CountDownLatch 래치다운, int loopCount) { this.latch = 래치; this.latchdown = 래치다운; this.loopCount = loopCount } @Override public void run() { // 신호 동기화 대기 try { this.latch .await(); } catch (InterruptedException e) { e.printStackTrace() } // for (int i = 0; i < loopCount; i++) { counter_integer.getAndIncrement(); } // 알림이 1씩 감소합니다. 래치다운.countDown(); } } // 스레드 클래스 IntegerThread는 Thread를 확장합니다. private CountDownLatch 래치다운 = null; ; 공개 IntegerThread(CountDownLatch 래치, CountDownLatch 래치다운, int loopCount) { this.latch = 래치; this.latchdown = 래치다운; this.loopCount = loopCount; } @Override public void run() { // 신호 동기화 대기 try { this.latch.await() } catch (InterruptedException e) { e.printStackTrace(); } // for (int i = 0; i < loopCount; i++) { count_int++ } // 알림이 1만큼 감소합니다.atchdown.countDown() } }}
일반 PC에서의 실행 결과는 다음과 유사합니다.
---------------testAtomic(); 두 개는 동일할 것으로 예상됩니다.------시간 소모: 85366msthreadCount = 100;loopCount = 10000600;sum = 1000060000;----testIntAdd(); 둘은 동일할 것으로 예상되지 않습니다------------ ----시간 소모: 1406msthreadCount = 100;loopCount = 10000600;sum = 119428988;
이를 통해 AtomicInteger 연산과 int 연산의 효율성 차이가 약 50~80배라는 것을 알 수 있습니다. 물론 int는 매우 시간이 많이 걸리는 비교입니다.
단일 스레드 실행으로 판단되면 int를 사용해야 하며 멀티 스레딩에서 int 연산 실행 효율성은 상당히 높으며 10억 번 실행하는 데 1.5초밖에 걸리지 않습니다.
(CPU가 2GHZ, 듀얼코어, 4스레드이고 이론상 최대치는 8GHZ라고 가정하면 이론적으로 초당 80억 클럭 사이클이 발생합니다.
10억 개의 Java int 증분에는 1.5초가 소요됩니다. 이는 120억 개의 작업에 해당하며, 각 주기는 12개의 CPU 주기를 소비합니다.
개인적으로 C언어도 4클럭(판단, 내부 코드 실행, 자동 증가 판단, 점프) 이상이 필요하다고 생각합니다.
전제는 JVM과 CPU가 근본적으로 최적화되지 않았다는 것입니다.
)
실제로 AtomicInteger의 효율성은 낮지 않습니다. 10억 번은 80초를 소비하고, 100만 번은 약 1000분의 80밀리초를 소비합니다.