“ อะตอมมิก” หมายถึงอะไรในการเขียนโปรแกรม


276

ในหนังสือ Java ที่มีประสิทธิภาพจะระบุ:

ข้อกำหนดทางภาษารับประกันว่าการอ่านหรือการเขียนตัวแปรนั้นเป็นอะตอมมิกเว้นแต่ตัวแปรนั้นเป็นประเภทlongหรือdouble[JLS, 17.4.7]

"atomic" หมายถึงอะไรในบริบทของการเขียนโปรแกรม Java หรือการเขียนโปรแกรมโดยทั่วไป?


24
ดำเนินการครั้งละหนึ่ง
Subhrajyoti Majumder

1
สามารถทำการดำเนินการครั้งเดียวเท่านั้นในตัวแปรในแต่ละครั้ง
kaysush


1
ฉันสงสัยคำถามปรัชญาอยู่ในcodereview.stackexchange.com
Phlip

การสังเกตว่าตัวแปรบางตัวไม่มีค่าเริ่มต้นที่มีการอ่านและเขียนแบบปรมาณูประกาศเป็นvolatile longหรือvolatile doubleทำให้การอ่านแบบปรมาณูและการเขียนแบบปรมาณู
H2ONaCl

คำตอบ:


372

นี่คือตัวอย่างเนื่องจากตัวอย่างมักชัดเจนกว่าคำอธิบายที่ยาว สมมติว่าเป็นตัวแปรประเภทfoo longการดำเนินการต่อไปนี้ไม่ใช่การดำเนินการแบบปรมาณู:

foo = 65465498L;

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

การทำให้การดำเนินการของอะตอมประกอบด้วยการใช้กลไกการซิงโครไนซ์เพื่อให้แน่ใจว่าการดำเนินการถูกมองเห็นจากเธรดอื่นใด ๆ ในรูปของอะตอมเดี่ยว (เช่นไม่สามารถแยกส่วนได้) ซึ่งหมายความว่าเธรดอื่น ๆ เมื่อการดำเนินการทำด้วยอะตอมจะเห็นค่าfooก่อนการมอบหมายหรือหลังการมอบหมาย แต่ไม่เคยมีค่ากลาง

วิธีง่ายๆในการทำเช่นนี้คือการทำให้ตัวแปรมีความผันผวน :

private volatile long foo;

หรือซิงโครไนซ์การเข้าถึงตัวแปร:

public synchronized void setFoo(long value) {
    this.foo = value;
}

public synchronized long getFoo() {
    return this.foo;
}
// no other use of foo outside of these two methods, unless also synchronized

หรือเพื่อแทนที่ด้วยAtomicLong:

private AtomicLong foo;

75
ดังนั้นนี่คือสมมติว่ามันกำลังทำงานในระบบ 32 บิต เกิดอะไรขึ้นถ้ามันเป็นระบบ 64 บิต จะ foo = 65465498L หรือไม่ ปรมาณูแล้ว?
Harke

46
@Harke หากคุณใช้ Java 64 บิตอยู่
Jeroen

4
สิ่งนี้ใช้ได้กับ C # และ. NET ด้วยหรือไม่ ถ้าใช่เพื่อที่จะได้รับพฤติกรรมของอะตอม CLR ต้องเป็น 64 บิต?
Fabiano

5
@Fabiano มันใช้งานและนี่คือวิธีการที่จะบรรลุมันใน. NET เนื่องจากเราไม่มีคำหลักที่ซิงโครไนซ์อย่าง Java stackoverflow.com/questions/541194/…
The Muffin Man

2
จากนั้นสมมติว่าเธรด A กำหนดค่ายาวจากนั้นในครึ่งทางเธรด B พยายามอ่าน หากการดำเนินการ A เป็นอะตอมมิฉะนั้นเธรด B จะรอจนกว่าจะเสร็จสิ้น? ซึ่งหมายความว่าการดำเนินงานของอะตอมจะให้ความปลอดภัยของเธรดโดยนัย
Teoman shipahi

60

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


25

มันเป็นสิ่งที่ "ปรากฏต่อส่วนที่เหลือของระบบที่จะเกิดขึ้นทันที" และอยู่ภายใต้การจัดประเภทของLinearizabilityในกระบวนการคำนวณ หากต้องการอ้างอิงบทความที่ลิงก์เพิ่มเติม:

Atomicity เป็นการรับประกันการแยกออกจากกระบวนการที่เกิดขึ้นพร้อมกัน นอกจากนี้การดำเนินงานปรมาณูมักจะมีคำจำกัดความประสบความสำเร็จหรือล้มเหลว - พวกเขาประสบความสำเร็จในการเปลี่ยนสถานะของระบบหรือไม่มีผลกระทบที่ชัดเจน

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

คำพูดของคุณเน้นว่าสิ่งนี้ไม่จำเป็นต้องเป็นพฤติกรรมที่คาดหวังในทุกกรณี


15

เพิ่งพบโพสต์การดำเนินการปรมาณูกับปลอดปรมาณูจะเป็นประโยชน์กับฉันมาก

"การดำเนินการกับหน่วยความจำที่ใช้ร่วมกันนั้นเป็นอะตอมถ้าทำในขั้นตอนเดียวที่สัมพันธ์กับเธรดอื่น ๆ

เมื่อมีการดำเนินการเก็บอะตอมมิกในหน่วยความจำที่ใช้ร่วมกันไม่มีเธรดอื่นสามารถสังเกตเห็นการปรับเปลี่ยนครึ่งเสร็จสมบูรณ์

เมื่อมีการดำเนินการโหลดปรมาณูในตัวแปรที่ใช้ร่วมกันมันจะอ่านค่าทั้งหมดตามที่ปรากฏในช่วงเวลาเดียวในเวลา "


14

หากคุณมีหลายเธรดที่ใช้งานวิธีการ m1 และ m2 ในรหัสด้านล่าง:

class SomeClass {
    private int i = 0;

    public void m1() { i = 5; }
    public int m2() { return i; }
}

คุณมีการรับประกันว่าการเรียกเธรดใด ๆm2จะอ่านเป็น 0 หรือ 5

ในทางกลับกันด้วยรหัสนี้ (ที่iยาว):

class SomeClass {
    private long i = 0;

    public void m1() { i = 1234567890L; }
    public long m2() { return i; }
}

การเรียกเธรดm2สามารถอ่าน 0, 1234567890L หรือค่าสุ่มอื่น ๆ ได้เนื่องจากคำสั่งi = 1234567890Lไม่รับประกันว่าจะเป็น atomic สำหรับlong(JVM สามารถเขียน 32 บิตแรกและ 32 บิตสุดท้ายในสองการดำเนินการและเธรดอาจสังเกตเห็นiในระหว่าง) .


ทำไมคุณถึงคิดว่า "ความยาว" ทำให้เกิดปัญหาในขณะที่ "int" ไม่ได้? โปรดดูที่นี่geekswithblogs.net/BlackRabbitCoder/archive/2012/08/09/…
onmyway133

1
@entropy การมอบหมายแบบยาวและแบบคู่ไม่รับประกันว่าจะเป็น atomic ใน Java ดังนั้นคุณสามารถอ่านได้นานโดยที่มีเพียงครึ่งหนึ่งของบิตที่ได้รับการอัปเดตหลังจากการมอบหมาย
assylias

0

ใน Java การอ่านและการเขียนฟิลด์ทุกประเภทยกเว้น long และ double เกิดขึ้นแบบ atom และถ้าฟิลด์ถูกประกาศด้วยตัวแก้ไขแบบระเหยแม้ long และ double จะอ่านและเขียนแบบ atom นั่นคือเราได้รับ 100% ไม่ว่าจะเกิดจากที่ใดหรือเกิดอะไรขึ้นที่นั่นและจะไม่มีผลลัพธ์ระหว่างกลางในตัวแปร

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