การอ้างอิงระเหยของ Java เทียบกับ AtomicReference


137

มีความแตกต่างระหว่างการvolatileอ้างอิงวัตถุหรือไม่AtomicReferenceในกรณีที่ฉันจะใช้get()และ - set()วิธีการจากAtomicReference?

คำตอบ:


114

คำตอบสั้น ๆ คือไม่

จากjava.util.concurrent.atomicเอกสารกำกับแพคเกจ อ้างถึง:

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

  • getมีผลต่อหน่วยความจำของการอ่านvolatileตัวแปร
  • setมีเอฟเฟกต์หน่วยความจำของการเขียน (กำหนด) volatileตัวแปร

อย่างไรก็ตามเอกสารนั้นดีมากและมีการอธิบายทุกอย่าง


AtomicReference::lazySetเป็นการดำเนินการที่ใหม่กว่า (Java 6+) ที่นำมาใช้ซึ่งมีความหมายที่ไม่สามารถเข้าใจได้ผ่านvolatileตัวแปร ดูโพสต์นี้สำหรับข้อมูลเพิ่มเติม


12
และคำตอบอีกต่อไปจะเป็นอย่างไร
Julien Grenier

เห็นด้วย อย่างน้อยเราต้องมีลิงค์
Julien Chastang

2
ลิงก์ไปยังคำตอบที่ยาวขึ้น: java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/atomic/…
Alex Siman

43

ไม่มีไม่มี

พลังเพิ่มเติมที่ให้มาโดย AtomicReference คือเมธอด CompareAndSet () และเพื่อน ๆ หากคุณไม่ต้องการวิธีการเหล่านั้นการอ้างอิงแบบระเหยจะให้ความหมายเช่นเดียวกับ AtomicReference.set () และ. get ()


14

มีความแตกต่างและการแลกเปลี่ยนหลายประการ:

  1. ใช้AtomicReferenceรับ / ชุดมีเหมือนกันความหมาย JMMเป็นฟิลด์ระเหย (เป็นรัฐ Javadoc), การ แต่AtomicReferenceเป็นเสื้อคลุมรอบการอ้างอิงเพื่อการเข้าถึงใด ๆ ไปยังเขตข้อมูลที่เกี่ยวข้องกับการไล่ล่าตัวชี้ต่อไป

  2. รอยความทรงจำคูณ (สมมติให้มีการบีบอัดสภาพแวดล้อม OOPS ที่เป็นจริงให้มากที่สุด VMs):

    • อ้างอิงระเหย = 4b
    • AtomicReference = 4b + 16b (ส่วนหัวของวัตถุ 12b + ฟิลด์อ้างอิง 4b)
  3. AtomicReferenceมี API ที่สมบูรณ์กว่าการอ้างอิงที่เปลี่ยนแปลงได้ คุณสามารถฟื้น API สำหรับการอ้างอิงระเหยโดยใช้AtomicFieldUpdaterหรือกับ Java VarHandle9 คุณยังสามารถเข้าถึงตัวตรงได้sun.misc.Unsafeหากคุณชอบวิ่งด้วยกรรไกร AtomicReferenceดำเนินการเองโดยใช้Unsafeไฟล์.

ดังนั้นเมื่อใดจึงควรเลือกอย่างใดอย่างหนึ่ง:

  • ต้องการเพียง get / set? ยึดติดกับสนามที่ระเหยง่ายที่สุดและมีค่าใช้จ่ายต่ำสุด
  • ต้องการฟังก์ชันพิเศษหรือไม่? หากนี่เป็นส่วนที่ละเอียดอ่อนด้านประสิทธิภาพ (ความเร็ว / หน่วยความจำค่าใช้จ่าย) ของรหัสของคุณให้เลือกระหว่างAtomicReference/ AtomicFieldUpdater/ Unsafeที่คุณมักจะจ่ายเงินด้วยความสามารถในการอ่านและความเสี่ยงที่จะได้รับ AtomicReferenceถ้าเรื่องนี้ไม่ได้เป็นเพียงแค่พื้นที่อ่อนไหวไป โดยทั่วไปผู้เขียนไลบรารีจะใช้วิธีการเหล่านี้ผสมผสานกันขึ้นอยู่กับ JDK ที่เป็นเป้าหมายข้อ จำกัด ของ API ที่คาดไว้ข้อ จำกัด ของหน่วยความจำและอื่น ๆ

7

ซอร์สโค้ด JDKเป็นหนึ่งในวิธีที่ดีที่สุดในการตอบความสับสนเช่นนี้ หากคุณดูโค้ดใน AtomicReference จะใช้ตัวแปร volatie สำหรับการจัดเก็บอ็อบเจ็กต์

private volatile V value;

เห็นได้ชัดว่าถ้าคุณจะใช้ get () และ set () ใน AtomicReference มันก็เหมือนกับการใช้ตัวแปรระเหย แต่ตามที่ผู้อ่านคนอื่น ๆ แสดงความคิดเห็น AtomicReference ให้ความหมาย CAS เพิ่มเติม ดังนั้นก่อนอื่นให้ตัดสินใจว่าคุณต้องการความหมายของ CAS หรือไม่และถ้าคุณทำเพียงอย่างเดียวแล้วใช้ AtomicReference


13
"ซอร์สโค้ด JDK เป็นวิธีที่ดีที่สุดวิธีหนึ่งในการตอบความสับสนเช่นนี้" => ฉันไม่จำเป็นต้องเห็นด้วย - javadoc (ซึ่งเป็นสัญญาของชั้นเรียน) เป็นวิธีที่ดีที่สุด สิ่งที่คุณพบในโค้ดตอบคำถามสำหรับการนำไปใช้งานเฉพาะ แต่โค้ดสามารถเปลี่ยนแปลงได้
assylias

4
ตัวอย่างเช่นตัวแปรนี้ในแฮชแมปมีความผันผวนใน JDK 6 แต่จะไม่ระเหยอีกต่อไปใน Java 7 คุณได้ใช้รหัสของคุณตามข้อเท็จจริงที่ว่าตัวแปรมีความผันผวนหรือไม่มันจะเสียเมื่ออัปเกรด JDK ของคุณ ... เป็นที่ยอมรับว่าตัวอย่างคือ แตกต่างกัน แต่คุณเข้าใจ
assylias

CAS นั้นเป็นตัวย่อมาตรฐานหรือไม่?
abbas

1
เปรียบเทียบและ Swap =)
สิ้นสุด

4

AtomicReferenceมีฟังก์ชันเพิ่มเติมที่ไม่มีตัวแปรระเหยธรรมดา ดังที่คุณได้อ่าน API Javadoc คุณจะรู้สิ่งนี้ แต่ยังมีการล็อคที่มีประโยชน์สำหรับการดำเนินการบางอย่าง

อย่างไรก็ตามหากคุณไม่ต้องการฟังก์ชันเพิ่มเติมนี้ฉันขอแนะนำให้คุณใช้volatileฟิลด์ธรรมดา


ดังนั้นความแตกต่างอยู่ที่ประสิทธิภาพของพวกเขา หากไม่มีความแตกต่างคุณจะไม่แนะนำให้ใช้อย่างใดอย่างหนึ่ง
BT

ประสิทธิภาพก็มากเหมือนกัน AtomicRefrence เพิ่มความซับซ้อนและการใช้หน่วยความจำ
Peter Lawrey

@BT volatileฟิลด์สามารถใช้งานได้เหมือนกับฟิลด์ทั่วไปในขณะที่การเข้าถึงค่าโดยAtomicReferenceต้องผ่านgetและsetวิธีการ
David Harkness

0

บางครั้งแม้ว่าคุณจะใช้แค่ gets และ set แต่ AtomicReference อาจเป็นทางเลือกที่ดี:

ตัวอย่างที่มีความผันผวน:

private volatile Status status;
...
public setNewStatus(Status newStatus){
  status = newStatus;
}

public void doSomethingConditionally() {
  if(status.isOk()){
    System.out.println("Status is ok: " + status); // here status might not be OK anymore because in the meantime some called setNewStatus(). setNewStatus should be synchronized
  }
}

การใช้งานกับ AtomicReference จะทำให้คุณมีการซิงโครไนซ์แบบ copy-on-write ฟรี

private AtomicReference<Status> statusWrapper;
...

public void doSomethingConditionally() {
  Status status = statusWrapper.get();
  if(status.isOk()){
    System.out.println("Status is ok: " + status); // here even if in the meantime some called setNewStatus() we're still referring to the old one
  }
}

อาจมีคนบอกว่าคุณยังสามารถมีสำเนาที่เหมาะสมได้หากคุณเปลี่ยนตัว:

Status status = statusWrapper.get();

กับ:

Status statusCopy = status;

อย่างไรก็ตามอันที่สองมีแนวโน้มที่จะถูกใครบางคนลบออกโดยไม่ได้ตั้งใจในอนาคตระหว่าง "การล้างโค้ด"

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