Java ได้ออกแบบคลาสบางส่วนสำหรับตัวนับความปลอดภัยเชิงตัวเลขภายใต้มัลติเธรด คลาสเหล่านี้เรียกว่าคลาสอะตอมมิก
java.util.concurrent.atomic.AtomicBoolean;java.util.concurrent.atomic.AtomicInteger;java.util.concurrent.atomic.AtomicLong;java.util.concurrent.atomic.AtomicReference;
ต่อไปนี้เป็นการทดสอบแบบเพิ่มหน่วยที่เปรียบเทียบ AtomicInteger และค่า int ทั่วไปภายใต้มัลติเธรด โดยใช้ junit4
รหัสที่สมบูรณ์:
แพ็คเกจ test.java;นำเข้า java.util.concurrent.CountDownLatch;นำเข้า java.util.concurrent.atomic.AtomicInteger;นำเข้า org.junit.Assert;นำเข้า org.junit.Before;นำเข้า org.junit.Test;/** * ทดสอบการดำเนินการที่เพิ่มขึ้นของ AtomicInteger และค่า int สามัญภายใต้มัลติเธรด*/คลาสสาธารณะ TestAtomic { // วัตถุการเพิ่มจำนวนเต็มอะตอม สาธารณะคงที่ AtomicInteger counter_integer; // = new AtomicInteger(0); // ตัวแปรประเภท int public static int count_int = 0; @Before public void setUp() { // ดำเนินการตั้งค่าเริ่มต้นก่อนที่การทดสอบทั้งหมดจะเริ่มต้น counter_integer = new AtomicInteger(0 } @Test โมฆะสาธารณะ testAtomic() พ่น InterruptedException { // จำนวนเธรดที่สร้าง int threadCount = 100; จำนวนครั้งที่เธรดในเครืออื่น ๆ วนซ้ำภายใน int loopCount = 10000600; // ควบคุมอ็อบเจ็กต์เสริมของเธรดในเครือ (เธรดอื่น ๆ รอการเริ่มต้นเธรดหลัก) CountDownLatch latch_1 = new CountDownLatch(1); object ของเธรดหลัก Object; (เธรดหลักรอให้เธรดย่อยทั้งหมดทำงานเสร็จก่อนดำเนินการต่อ) CountDownLatch latch_n = new CountDownLatch(threadCount); // สร้างและเริ่มเธรดย่อยอื่น ๆ สำหรับ (int i = 0; i < threadCount; i++) { Thread thread = new AtomicIntegerThread(latch_1, latch_n, loopCount); } long startNano = System.nanoTime(); ; // รอให้เธรดอื่นเสร็จสิ้น latch_n.await(); // long endNano = System.nanoTime(); counter_integer.get(); // Assert.assertEquals("ผลรวมไม่เท่ากับ threadCount * loopCount, การทดสอบล้มเหลว", sum, threadCount * loopCount("--------testAtomic( คาดว่าจะเท่ากัน------------"); System.out.println("ใช้เวลานาน: " + ((endNano - startNano) / (1,000 * 1,000)) + "ms" ); System.out.println("threadCount = " + (threadCount) + ";"); System.out.println("loopCount = " + (loopCount) + ";"); System.out.println("sum = " + (ผลรวม) + ";"); } @Test โมฆะสาธารณะ testIntAdd() พ่น InterruptedException { // จำนวนเธรดที่สร้าง int threadCount = 100; จำนวนครั้งที่เธรดในเครืออื่น ๆ วนซ้ำภายใน int loopCount = 10000600; // ควบคุมอ็อบเจ็กต์เสริมของเธรดในเครือ (เธรดอื่น ๆ รอการเริ่มต้นเธรดหลัก) CountDownLatch latch_1 = new CountDownLatch(1); object ของเธรดหลัก Object; (เธรดหลักรอให้เธรดย่อยทั้งหมดทำงานเสร็จก่อนดำเนินการต่อ) CountDownLatch latch_n = new CountDownLatch(threadCount); // สร้างและเริ่มเธรดย่อยอื่น ๆ สำหรับ (int i = 0; i < threadCount; i++) { Thread thread = new IntegerThread(latch_1, latch_n, loopCount); } long startNano = System.nanoTime(); ); // รอให้เธรดอื่นเสร็จสิ้น latch_n.await(); // 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 = " + (ผลรวม) + ";"); } // คลาสเธรด AtomicIntegerThread ขยายเธรด { ส่วนตัว CountDownLatch latch = null; ส่วนตัว CountDownLatch latchdown = null; AtomicIntegerThread (CountDownLatch latch, CountDownLatch latchdown, int loopCount) { this.latch = latch; this.latchdown = latchdown; this.loopCount = loopCount; } @ แทนที่การรันสาธารณะ () { // รอการซิงโครไนซ์สัญญาณ { this.latch . await(); } catch (InterruptedException e) { e.printStackTrace(); } // สำหรับ (int i = 0; i < loopCount; i++) { counter_integer.getAndIncreation(); } // การแจ้งเตือนลดลง 1 latchdown.countDown(); } } // Thread class IntegerThread { private CountDownLatch latchdown = null; ; สาธารณะ IntegerThread (CountDownLatch latch, CountDownLatch latchdown, int loopCount) { this.latch = latch; this.latchdown = latchdown; this.loopCount = loopCount; } @Override public void run() { // รอการซิงโครไนซ์สัญญาณ ลอง { this.latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } // for (int i = 0; i < loopCount; i++) { count_int++; } // การแจ้งเตือนลดลง 1 latchdown.countDown();
ผลการดำเนินการบนพีซีธรรมดาจะคล้ายกับดังต่อไปนี้:
--------------- testAtomic(); คาดว่าสองจะเท่ากัน ------------------ ใช้เวลานาน: 85366msthreadCount = 100;loopCount = 10000600;sum = 1000060000;----------------- testIntAdd(); ---- ใช้เวลานาน: 1406msthreadCount = 100;loopCount = 10000600;sum = 119428988;
จากนี้จะเห็นได้ว่าความแตกต่างด้านประสิทธิภาพระหว่างการดำเนินการ AtomicInteger และการดำเนินการ int อยู่ที่ประมาณ 50-80 เท่า แน่นอนว่า int ใช้เวลานานมากในการอ้างอิงเท่านั้น
หากถูกกำหนดให้เป็นการประมวลผลแบบเธรดเดียว ควรใช้ int และประสิทธิภาพของการดำเนินการ int ภายใต้มัลติเธรดนั้นค่อนข้างสูงและใช้เวลาเพียง 1.5 วินาทีต่อ 1 พันล้านครั้ง
(สมมติว่า CPU คือ 2GHZ แบบดูอัลคอร์และ 4 เธรด และสูงสุดตามทฤษฎีคือ 8GHZ ตามทฤษฎีแล้วจะมีความเร็วสัญญาณนาฬิกา 8 พันล้านรอบต่อวินาที
การเพิ่ม Int ของ Java หนึ่งพันล้านครั้งจะใช้เวลา 1.5 วินาที ซึ่งเท่ากับการดำเนินการ 12 พันล้านรายการ โดยแต่ละรอบจะใช้ CPU 12 รอบ
โดยส่วนตัวแล้ว ฉันคิดว่าประสิทธิภาพนั้นดี ภาษา C ควรต้องใช้มากกว่า 4 รอบนาฬิกา (การตัดสิน การเรียกใช้โค้ดภายใน การตัดสินการเพิ่มค่าอัตโนมัติ การข้าม)
สถานที่ตั้งคือ: JVM และ CPU ไม่ได้รับการปรับให้เหมาะสมที่สุด
-
ในความเป็นจริง ประสิทธิภาพของ AtomicInteger ไม่ได้ต่ำเลย หนึ่งพันล้านครั้งใช้เวลา 80 วินาที และหนึ่งล้านครั้งก็เท่ากับประมาณหนึ่งในพันหรือ 80 มิลลิวินาที