คำตอบ:
เมื่อมีหลายเธรดจำเป็นต้องตรวจสอบและเปลี่ยนบูลีน ตัวอย่างเช่น:
if (!initialized) {
initialize();
initialized = true;
}
สิ่งนี้ไม่ปลอดภัยสำหรับเธรด คุณสามารถแก้ไขได้โดยใช้AtomicBoolean
:
if (atomicInitialized.compareAndSet(false, true)) {
initialize();
}
true
เมื่อinitialize()
ยังไม่เสร็จสมบูรณ์ ดังนั้นจึงทำงานได้เฉพาะในกรณีที่หัวข้ออื่น ๆ initialize()
ไม่สนใจเกี่ยวกับความสำเร็จของ
initialized
ใช้เพื่อให้แน่ใจว่ามีเพียงหนึ่งกระทู้เท่านั้นที่จะเรียกใช้initialize()
เมธอด เห็นได้ชัดว่าinitialized
เป็นจริงไม่ได้หมายความว่าการเริ่มต้นที่ได้เสร็จสิ้นแน่นอนในกรณีนี้จึงอาจจะเป็นระยะเวลาที่แตกต่างกันออกไปเล็กน้อยจะดีกว่าที่นี่ อีกครั้งมันขึ้นอยู่กับสิ่งที่มันถูกใช้สำหรับ
volatile boolean
เหมือนAtomicBoolean
หรือไม่
synchronized
บล็อกซึ่งในกรณีที่คุณไม่จำเป็นต้องมีเพียงAtomicBoolean
volatile boolean
( if(! this.initialized) { synchronized(this) { if(! this.initialized) { initialize(); this.initialized = true; } } }
จะให้แน่ใจว่ามีเพียงหนึ่งโทรด้ายinitialize
และหัวข้ออื่น ๆ ที่รอคอยให้ได้กระทำเพื่อให้ว่าinitialized
มีการทำเครื่องหมายvolatile
.)
นี่คือบันทึก (จากหนังสือ Brian Goetz ) ที่ฉันทำนั่นอาจช่วยคุณได้
คลาส AtomicXXX
ให้การใช้งานแบบไม่บล็อคเปรียบเทียบและสลับ
รับประโยชน์จากการสนับสนุนที่จัดทำโดยฮาร์ดแวร์ (คำสั่ง CMPXCHG บน Intel) เมื่อเธรดจำนวนมากกำลังทำงานผ่านรหัสของคุณที่ใช้ API การทำงานพร้อมกันของอะตอมเหล่านี้พวกเขาจะปรับมาตราส่วนได้ดีกว่ารหัสที่ใช้การตรวจสอบระดับวัตถุ เนื่องจากกลไกการซิงโครไนซ์ของ Java ทำให้การรอโค้ดเมื่อมีเธรดจำนวนมากที่ทำงานผ่านส่วนที่สำคัญของคุณจำนวนเวลา CPU ที่ใช้ในการจัดการกลไกการซิงโครไนซ์นั้นเอง (รอการแจ้งเตือนและอื่น ๆ ) เนื่องจาก API ใหม่ใช้การสร้างระดับฮาร์ดแวร์ (ตัวแปรอะตอมมิก) และรอและล็อกอัลกอริธึมฟรีเพื่อใช้ความปลอดภัยของเธรดจึงใช้เวลา CPU มากขึ้นในการ "ทำสิ่งต่าง ๆ " แทนที่จะจัดการซิงโครไนซ์
ไม่เพียงเสนอทรูพุตที่ดีขึ้น แต่ยังให้ความต้านทานต่อปัญหาชีวิตเช่นการหยุดชะงักและการกลับลำดับความสำคัญ
มีสองเหตุผลหลักที่คุณสามารถใช้บูลีนแบบอะตอมมิก ก่อนอื่นมันไม่แน่นอนคุณสามารถส่งผ่านมันเป็นข้อมูลอ้างอิงและเปลี่ยนค่าที่เกี่ยวข้องกับบูลีนเอง
public final class MyThreadSafeClass{
private AtomicBoolean myBoolean = new AtomicBoolean(false);
private SomeThreadSafeObject someObject = new SomeThreadSafeObject();
public boolean doSomething(){
someObject.doSomeWork(myBoolean);
return myBoolean.get(); //will return true
}
}
และในคลาส someObject
public final class SomeThreadSafeObject{
public void doSomeWork(AtomicBoolean b){
b.set(true);
}
}
ที่สำคัญกว่านั้นคือเธรดของตนนั้นปลอดภัยและสามารถบ่งบอกถึงนักพัฒนาที่รักษาคลาสได้ว่าตัวแปรนี้คาดว่าจะถูกแก้ไขและอ่านจากหลายเธรด หากคุณไม่ได้ใช้ AtomicBoolean คุณต้องซิงโครไนซ์ตัวแปรบูลีนที่คุณใช้โดยการประกาศให้มันมีความผันผวนหรือซิงโครไนซ์รอบ ๆ การอ่านและเขียนของฟิลด์
AtomicBoolean
ชั้นช่วยให้คุณมีค่าบูลีนที่คุณสามารถอัปเดตอะตอม ใช้มันเมื่อคุณมีหลายเธรดที่เข้าถึงตัวแปรบูลีน
ภาพรวมแพคเกจ java.util.concurrent.atomicช่วยให้คุณมีคำอธิบายที่ดีระดับสูงของสิ่งที่เรียนในแพคเกจนี้ทำอย่างไรและเมื่อจะใช้พวกเขา ฉันอยากจะแนะนำหนังสือJava Concurrency ในทางปฏิบัติโดย Brian Goetz
ตัดตอนมาจากคำอธิบายแพคเกจ
แพ็กเกจ java.util.concurrent.atomic คำอธิบาย: ชุดเครื่องมือขนาดเล็กของคลาสที่รองรับการเขียนโปรแกรมล็อคเธรดที่ปลอดภัยโดยไม่มีการล็อคตัวแปรเดียว [... ]
ข้อมูลจำเพาะของวิธีการเหล่านี้ช่วยให้การใช้งานสามารถใช้คำแนะนำอะตอมมิกระดับเครื่องจักรที่มีประสิทธิภาพซึ่งมีอยู่ในโปรเซสเซอร์ปัจจุบัน [... ]
อินสแตนซ์ของคลาส AtomicBoolean, AtomicInteger, AtomicLong และ AtomicReference แต่ละรายการจะให้การเข้าถึงและอัปเดตตัวแปรเดียวของประเภทที่สอดคล้องกัน [... ]
เอฟเฟกต์หน่วยความจำสำหรับการเข้าถึงและการอัพเดทอะตอมมิกส์มักทำตามกฎสำหรับสารระเหย:
- รับมีผลกระทบหน่วยความจำของการอ่านตัวแปรระเหย
- ชุดมีผลกระทบหน่วยความจำของการเขียน (กำหนด) ตัวแปรระเหย
- weakCompareAndSet แบบ atom อ่านและเขียนตัวแปรแบบมีเงื่อนไขได้รับคำสั่งเกี่ยวกับการดำเนินการของหน่วยความจำอื่นในตัวแปรนั้น แต่อย่างอื่นทำหน้าที่เป็นการดำเนินการหน่วยความจำแบบไม่ลบเลือน
- CompareAndSet และการดำเนินการอ่านและอัปเดตอื่น ๆ ทั้งหมดเช่น getAndIncrement มีผลต่อหน่วยความจำของทั้งการอ่านและการเขียนตัวแปรระเหย
volatile boolean
vsAtomicBoolean
: stackoverflow.com/questions/3786825/…