AtomicBoolean ทำอะไรที่บูลีนระเหยไม่สามารถบรรลุได้
AtomicBoolean ทำอะไรที่บูลีนระเหยไม่สามารถบรรลุได้
คำตอบ:
พวกมันต่างกันโดยสิ้นเชิง ลองพิจารณาตัวอย่างของvolatile
จำนวนเต็มนี้:
volatile int i = 0;
void incIBy5() {
i += 5;
}
หากสองเธรดเรียกใช้ฟังก์ชันพร้อมกันi
อาจเป็น 5 หลังจากนั้นเนื่องจากโค้ดที่คอมไพล์จะคล้ายกับสิ่งนี้ (ยกเว้นคุณไม่สามารถซิงโครไนซ์ได้int
):
void incIBy5() {
int temp;
synchronized(i) { temp = i }
synchronized(i) { i = temp + 5 }
}
หากตัวแปรมีความผันผวนการเข้าถึงแบบอะตอมมิกทุกครั้งจะถูกซิงโครไนซ์ แต่ก็ไม่ชัดเจนเสมอไปว่าสิ่งใดที่มีคุณสมบัติเป็นการเข้าถึงแบบอะตอมมิก ด้วยAtomic*
วัตถุรับประกันว่าทุกวิธีคือ "atomic"
ดังนั้นหากคุณใช้AtomicInteger
และคุณสามารถมั่นใจได้ว่าผลจะเป็นgetAndAdd(int delta)
10
ในทำนองเดียวกันถ้าทั้งสองเธรดคัดค้านboolean
ตัวแปรพร้อมกันโดยAtomicBoolean
คุณสามารถมั่นใจได้ว่ามันมีค่าดั้งเดิมหลังจากนั้นด้วย a volatile boolean
คุณไม่สามารถทำได้
ดังนั้นเมื่อใดก็ตามที่คุณมีมากกว่าหนึ่งเธรดที่ปรับเปลี่ยนฟิลด์คุณต้องทำให้เป็นแบบอะตอมหรือใช้การซิงโครไนซ์อย่างชัดเจน
วัตถุประสงค์ของการvolatile
เป็นที่แตกต่างกัน ลองพิจารณาตัวอย่างนี้
volatile boolean stop = false;
void loop() {
while (!stop) { ... }
}
void stop() { stop = true; }
หากคุณมีการรันเธรดloop()
และการเรียกเธรดอื่นstop()
คุณอาจเรียกใช้การวนซ้ำไม่สิ้นสุดหากคุณละเว้นvolatile
เนื่องจากเธรดแรกอาจแคชค่าหยุด ที่นี่volatile
คำแนะนำสำหรับคอมไพเลอร์จะต้องระมัดระวังในการปรับแต่งให้ดีขึ้น
volatile
คำถามคือไม่ได้เกี่ยวกับ คำถามคือเกี่ยวกับVSvolatile boolean
AtomicBoolean
volatile boolean
นั่นก็เพียงพอแล้ว AtomicBoolean
หากยังมีนักเขียนหลายคนคุณอาจต้อง
ฉันใช้เขตข้อมูลระเหยเมื่อเขตข้อมูลกล่าวว่ามีการปรับปรุงโดยเธรดเจ้าของเท่านั้นและค่าถูกอ่านโดยเธรดอื่นเท่านั้นคุณสามารถคิดว่ามันเป็นสถานการณ์เผยแพร่ / สมัครสมาชิกที่มีผู้สังเกตการณ์จำนวนมาก แต่มีผู้เผยแพร่เพียงคนเดียว อย่างไรก็ตามหากผู้สังเกตการณ์เหล่านั้นต้องปฏิบัติตามตรรกะบางอย่างตามค่าของสนามแล้วผลักกลับค่าใหม่จากนั้นฉันก็ไปกับ Atomic * vars หรือล็อคหรือบล็อกซิงโครไนซ์ไม่ว่าอะไรจะเหมาะกับฉันที่สุด ในสถานการณ์ที่เกิดขึ้นพร้อมกันหลายอย่างมันจะเดือดลงเพื่อรับค่าให้เปรียบเทียบกับอีกสถานการณ์หนึ่งและอัพเดทหากจำเป็นดังนั้นเมธอด comparAndSet และ getAndSet จะปรากฏในคลาส Atomic *
ตรวจสอบ JavaDocs ของแพคเกจjava.util.concurrent.atomicสำหรับรายการของคลาส Atomic และคำอธิบายที่ยอดเยี่ยมเกี่ยวกับวิธีการทำงานของมัน (เพิ่งรู้ว่าพวกเขาไม่ล็อคดังนั้นจึงมีข้อดีเหนือล็อคหรือบล็อกที่ซิงโครไนซ์)
boolean
var volatile boolean
เราควรเลือก
คุณไม่สามารถทำcompareAndSet
, getAndSet
การดำเนินงานของอะตอมกับบูลระเหย (ยกเว้นกรณีของหลักสูตรที่คุณประสาน)
AtomicBoolean
มีวิธีการดำเนินการผสมของพวกเขาเป็นอะตอมและไม่ต้องใช้synchronized
บล็อก ในทางกลับกันvolatile boolean
สามารถดำเนินการผสมได้หากทำได้ภายในsynchronized
บล็อก
ผลกระทบหน่วยความจำของการอ่าน / เขียนvolatile boolean
เหมือนกับget
และset
วิธีการAtomicBoolean
ตามลำดับ
ตัวอย่างเช่นcompareAndSet
วิธีที่จะดำเนินการตามปกติในอะตอม (ไม่มีsynchronized
บล็อก):
if (value == expectedValue) {
value = newValue;
return true;
} else {
return false;
}
ดังนั้นcompareAndSet
วิธีการนี้จะช่วยให้คุณเขียนโค้ดที่รับประกันว่าจะรันเพียงครั้งเดียวแม้ว่าจะถูกเรียกจากหลายเธรด ตัวอย่างเช่น:
final AtomicBoolean isJobDone = new AtomicBoolean(false);
...
if (isJobDone.compareAndSet(false, true)) {
listener.notifyJobDone();
}
รับประกันว่าจะแจ้งให้ผู้ฟังทราบเพียงครั้งเดียว (สมมติว่าไม่มีเธรดอื่นตั้งค่าAtomicBoolean
กลับไปเป็นfalse
อีกครั้งหลังจากที่ตั้งค่าเป็นtrue
)
volatile
คำหลักรับประกันว่าเกิดขึ้นก่อนความสัมพันธ์ระหว่างเธรดที่แชร์ตัวแปรนั้น ไม่รับประกันว่าจะมี 2 กระทู้หรือมากกว่านั้นจะไม่รบกวนซึ่งกันและกันขณะเข้าถึงตัวแปรบูลีน
Atomic*
ระดับ wraps volatile
ฟิลด์
บูลีนระเหยกับ AtomicBoolean
คลาส Atomic * จะตัดค่าดั้งเดิมของชนิดเดียวกัน จากแหล่งที่มา:
public class AtomicLong extends Number implements java.io.Serializable {
...
private volatile long value;
...
public final long get() {
return value;
}
...
public final void set(long newValue) {
value = newValue;
}
ดังนั้นหากสิ่งที่คุณทำคือการได้รับและการตั้งค่า Atomic * คุณก็อาจมีเขตข้อมูลระเหยแทน
AtomicBoolean ทำอะไรที่บูลีนระเหยไม่สามารถบรรลุได้
เรียน * อะตอมให้คุณวิธีการที่ให้การทำงานที่สูงขึ้นเช่นincrementAndGet()
,compareAndSet()
และอื่น ๆ ที่ใช้การดำเนินงานหลาย (รับ / เพิ่ม / ชุดทดสอบ / ชุด) โดยไม่ได้ล็อค นั่นเป็นเหตุผลที่คลาส Atomic * นั้นทรงพลัง
ตัวอย่างเช่นหากมีหลายเธรดที่ใช้รหัสต่อไปนี้โดยใช้ ++
จะมีเงื่อนไขการแข่งขันเนื่องจาก++
เป็นจริง: รับเพิ่มและตั้งค่า
private volatile value;
...
// race conditions here
value++;
อย่างไรก็ตามรหัสต่อไปนี้จะทำงานในสภาพแวดล้อมแบบมัลติเธรดได้อย่างปลอดภัยโดยไม่ต้องล็อค:
private final AtomicLong value = new AtomicLong();
...
value.incrementAndGet();
สิ่งสำคัญคือต้องทราบว่าการตัดฟิลด์ที่ระเหยได้ของคุณโดยใช้คลาส Atomic * เป็นวิธีที่ดีในการสรุปทรัพยากรที่สำคัญร่วมกันจากมุมมองวัตถุ ซึ่งหมายความว่าผู้พัฒนาไม่สามารถจัดการกับเขตข้อมูลโดยสมมติว่าไม่มีการใช้งานร่วมกันอาจทำให้เกิดปัญหากับฟิลด์ ++; หรือรหัสอื่น ๆ ที่แนะนำสภาพการแข่งขัน
หากมีหลายเธรดที่เข้าถึงตัวแปรระดับคลาสดังนั้นแต่ละเธรดสามารถเก็บสำเนาของตัวแปรนั้นไว้ในแคช threadlocal
การทำให้ตัวแปรระเหยง่ายจะป้องกันไม่ให้เธรดเก็บสำเนาของตัวแปรไว้ในแคชโลคอล
ตัวแปรอะตอมนั้นแตกต่างกันและอนุญาตให้แก้ไขค่าของอะตอมได้
บูลีนดึกดำบรรพ์เป็นอะตอมมิกสำหรับการเขียนและการอ่านการระเหยรับประกันหลักการที่เกิดขึ้นก่อน ดังนั้นหากคุณต้องการ get () และ set () อย่างง่ายคุณไม่จำเป็นต้องใช้ AtomicBoolean
ในทางตรงกันข้ามถ้าคุณจำเป็นต้องใช้การตรวจสอบบางอย่างก่อนที่จะตั้งค่าของตัวแปรเช่น "ถ้าเป็นจริงแล้วตั้งค่าเป็นเท็จ" จากนั้นคุณจะต้องดำเนินการนี้ atomically เช่นกันในกรณีนี้ใช้ comparAndSet และวิธีการอื่น ๆ AtomicBoolean เนื่องจากถ้าคุณพยายามใช้ตรรกะนี้ด้วยบูลีนที่ระเหยคุณจะต้องมีการซิงโครไนซ์บางอย่างเพื่อให้แน่ใจว่าค่าไม่เปลี่ยนแปลงระหว่างการรับและการตั้งค่า
จำ IDIOM -
อ่าน - แก้ไข - เขียนสิ่งนี้คุณไม่สามารถทำได้ด้วยความผันผวน
volatile
ใช้งานได้เฉพาะในกรณีที่เธรดเจ้าของมีความสามารถในการอัปเดตค่าฟิลด์และเธรดอื่นสามารถอ่านได้เท่านั้น
หากคุณมีเพียงหนึ่งเธรดที่แก้ไขบูลีนของคุณคุณสามารถใช้บูลีนที่ระเหยได้ (โดยปกติคุณจะทำสิ่งนี้เพื่อกำหนดstop
ตัวแปรที่เลือกในลูปหลักของเธรด)
AtomicBoolean
แต่ถ้าคุณมีหลายหัวข้อการปรับเปลี่ยนแบบบูลที่คุณควรใช้ มิฉะนั้นรหัสต่อไปนี้ไม่ปลอดภัย:
boolean r = !myVolatileBoolean;
การดำเนินการนี้ทำได้สองขั้นตอน:
หากเธรดอื่นแก้ไขค่าระหว่าง#1
และ2#
คุณอาจได้รับผลลัพธ์ที่ผิด AtomicBoolean
วิธีการหลีกเลี่ยงปัญหานี้โดยทำตามขั้นตอน#1
และ#2
atomically
ทั้งคู่มีแนวคิดเดียวกัน แต่ในบูลีนแบบอะตอมมิกมันจะให้อะตอมมิกซิตี้แก่การทำงานในกรณีที่สวิตช์ซีพียูเกิดขึ้นระหว่างนั้น