ฉันต้องใช้ AtomicBoolean ใน Java เมื่อใด


คำตอบ:


244

เมื่อมีหลายเธรดจำเป็นต้องตรวจสอบและเปลี่ยนบูลีน ตัวอย่างเช่น:

if (!initialized) {
   initialize();
   initialized = true;
}

สิ่งนี้ไม่ปลอดภัยสำหรับเธรด คุณสามารถแก้ไขได้โดยใช้AtomicBoolean:

if (atomicInitialized.compareAndSet(false, true)) {
    initialize();
}

51
มันดูไม่เหมือนตัวอย่างในโลกแห่งความจริง - เธรดอื่นสามารถดูได้trueเมื่อinitialize()ยังไม่เสร็จสมบูรณ์ ดังนั้นจึงทำงานได้เฉพาะในกรณีที่หัวข้ออื่น ๆ initialize()ไม่สนใจเกี่ยวกับความสำเร็จของ
axtavt

6
@axtavt: ฉันคิดว่ามันเป็นตัวอย่างที่ถูกต้องในโลกแห่งความจริงหากinitializedใช้เพื่อให้แน่ใจว่ามีเพียงหนึ่งกระทู้เท่านั้นที่จะเรียกใช้initialize()เมธอด เห็นได้ชัดว่าinitializedเป็นจริงไม่ได้หมายความว่าการเริ่มต้นที่ได้เสร็จสิ้นแน่นอนในกรณีนี้จึงอาจจะเป็นระยะเวลาที่แตกต่างกันออกไปเล็กน้อยจะดีกว่าที่นี่ อีกครั้งมันขึ้นอยู่กับสิ่งที่มันถูกใช้สำหรับ
ColinD

14
คุณจะต้องใช้ 2 booleans สำหรับ initStarted และ initCompleted จากนั้นชุดเธรดแรกที่เริ่มต้น initStarted และเรียก initialise () ส่วนที่เหลือรอจนกว่า initCompleted จะเป็นจริง
Martin

3
@Bozho - อ่านและเขียนไปยังเขตข้อมูลบูลีนเป็นแบบปรมาณูใช่ไหมตอนนี้ความเปลี่ยนแปลงทำให้ฉันมีค่าล่าสุดของเขตข้อมูลบูลีน ดังนั้นจะไม่volatile booleanเหมือนAtomicBooleanหรือไม่
TheLostMind

2
@ มาร์ติน: ไม่มีทางที่จะรอบูลีนให้กลายเป็นจริง คุณต้องการกลไกเพิ่มเติม วิธีการที่เหมาะสมที่สุดคือการใช้synchronizedบล็อกซึ่งในกรณีที่คุณไม่จำเป็นต้องมีเพียงAtomicBoolean volatile boolean( if(! this.initialized) { synchronized(this) { if(! this.initialized) { initialize(); this.initialized = true; } } }จะให้แน่ใจว่ามีเพียงหนึ่งโทรด้ายinitializeและหัวข้ออื่น ๆ ที่รอคอยให้ได้กระทำเพื่อให้ว่าinitializedมีการทำเครื่องหมายvolatile.)
ruakh

52

นี่คือบันทึก (จากหนังสือ Brian Goetz ) ที่ฉันทำนั่นอาจช่วยคุณได้

คลาส AtomicXXX

  • ให้การใช้งานแบบไม่บล็อคเปรียบเทียบและสลับ

  • รับประโยชน์จากการสนับสนุนที่จัดทำโดยฮาร์ดแวร์ (คำสั่ง CMPXCHG บน Intel) เมื่อเธรดจำนวนมากกำลังทำงานผ่านรหัสของคุณที่ใช้ API การทำงานพร้อมกันของอะตอมเหล่านี้พวกเขาจะปรับมาตราส่วนได้ดีกว่ารหัสที่ใช้การตรวจสอบระดับวัตถุ เนื่องจากกลไกการซิงโครไนซ์ของ Java ทำให้การรอโค้ดเมื่อมีเธรดจำนวนมากที่ทำงานผ่านส่วนที่สำคัญของคุณจำนวนเวลา CPU ที่ใช้ในการจัดการกลไกการซิงโครไนซ์นั้นเอง (รอการแจ้งเตือนและอื่น ๆ ) เนื่องจาก API ใหม่ใช้การสร้างระดับฮาร์ดแวร์ (ตัวแปรอะตอมมิก) และรอและล็อกอัลกอริธึมฟรีเพื่อใช้ความปลอดภัยของเธรดจึงใช้เวลา CPU มากขึ้นในการ "ทำสิ่งต่าง ๆ " แทนที่จะจัดการซิงโครไนซ์

  • ไม่เพียงเสนอทรูพุตที่ดีขึ้น แต่ยังให้ความต้านทานต่อปัญหาชีวิตเช่นการหยุดชะงักและการกลับลำดับความสำคัญ


34

มีสองเหตุผลหลักที่คุณสามารถใช้บูลีนแบบอะตอมมิก ก่อนอื่นมันไม่แน่นอนคุณสามารถส่งผ่านมันเป็นข้อมูลอ้างอิงและเปลี่ยนค่าที่เกี่ยวข้องกับบูลีนเอง

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 คุณต้องซิงโครไนซ์ตัวแปรบูลีนที่คุณใช้โดยการประกาศให้มันมีความผันผวนหรือซิงโครไนซ์รอบ ๆ การอ่านและเขียนของฟิลด์


4
สำหรับความรักของพระเจ้านั่นเป็นเพียงการแสดงความไม่แน่นอนของวัตถุนั้น ฉันเขียนว่าเพื่อวัตถุประสงค์ในการสาธิตโดยเฉพาะ
John Vint

และยิ่งไปกว่านั้นถ้านั่นคือสิ่งที่เกิดขึ้นแล้วใช่มันจะกลับมาจริงเสมอ
John Vint

นั่นไม่ได้พิสูจน์ว่ามันเป็นหรือไม่ปลอดภัยเธรด ฉันสามารถเขียนเกร็ดเล็กเกร็ดน้อยของฉันให้เสร็จเพื่อทำให้ชั้นเรียนมีความปลอดภัยต่อเธรดมาก แต่นั่นก็ฆ่าจุดของฉันได้
John Vint

1
ฉันคิดว่าเพียงความผันผวนไม่เพียงพอ คิดเกี่ยวกับสถานการณ์ที่เธรดสองตัวที่อ่านและเขียนค่าเดียวกันโดยตรงจากหน่วยความจำหลักไม่มีการซิงค์ระหว่างเธรดเหล่านั้น - อาจเกิดปัญหาการเกิดขึ้นพร้อมกัน
Shay Tsadok

1
คุณพูดถูกมันคงไม่เพียงพอสำหรับชุดอะตอมแล้วตรวจสอบการทำงานแม้ว่าจะมีบริบทไม่เพียงพอจาก OP เพื่อให้สมมติฐานนั้น ในการพูดความผันผวนอาจไม่เพียงพอจริงเสมอขึ้นอยู่กับสถานการณ์
John Vint

18

AtomicBooleanชั้นช่วยให้คุณมีค่าบูลีนที่คุณสามารถอัปเดตอะตอม ใช้มันเมื่อคุณมีหลายเธรดที่เข้าถึงตัวแปรบูลีน

ภาพรวมแพคเกจ java.util.concurrent.atomicช่วยให้คุณมีคำอธิบายที่ดีระดับสูงของสิ่งที่เรียนในแพคเกจนี้ทำอย่างไรและเมื่อจะใช้พวกเขา ฉันอยากจะแนะนำหนังสือJava Concurrency ในทางปฏิบัติโดย Brian Goetz


5

ตัดตอนมาจากคำอธิบายแพคเกจ

แพ็กเกจ java.util.concurrent.atomic คำอธิบาย: ชุดเครื่องมือขนาดเล็กของคลาสที่รองรับการเขียนโปรแกรมล็อคเธรดที่ปลอดภัยโดยไม่มีการล็อคตัวแปรเดียว [... ]

ข้อมูลจำเพาะของวิธีการเหล่านี้ช่วยให้การใช้งานสามารถใช้คำแนะนำอะตอมมิกระดับเครื่องจักรที่มีประสิทธิภาพซึ่งมีอยู่ในโปรเซสเซอร์ปัจจุบัน [... ]

อินสแตนซ์ของคลาส AtomicBoolean, AtomicInteger, AtomicLong และ AtomicReference แต่ละรายการจะให้การเข้าถึงและอัปเดตตัวแปรเดียวของประเภทที่สอดคล้องกัน [... ]

เอฟเฟกต์หน่วยความจำสำหรับการเข้าถึงและการอัพเดทอะตอมมิกส์มักทำตามกฎสำหรับสารระเหย:

  • รับมีผลกระทบหน่วยความจำของการอ่านตัวแปรระเหย
  • ชุดมีผลกระทบหน่วยความจำของการเขียน (กำหนด) ตัวแปรระเหย
  • weakCompareAndSet แบบ atom อ่านและเขียนตัวแปรแบบมีเงื่อนไขได้รับคำสั่งเกี่ยวกับการดำเนินการของหน่วยความจำอื่นในตัวแปรนั้น แต่อย่างอื่นทำหน้าที่เป็นการดำเนินการหน่วยความจำแบบไม่ลบเลือน
  • CompareAndSet และการดำเนินการอ่านและอัปเดตอื่น ๆ ทั้งหมดเช่น getAndIncrement มีผลต่อหน่วยความจำของทั้งการอ่านและการเขียนตัวแปรระเหย
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.