Volatile vs Static ใน Java


265

มันถูกต้องหรือไม่ที่จะบอกว่านั่นstaticหมายถึงหนึ่งสำเนาของค่าสำหรับวัตถุทั้งหมดและvolatileหมายถึงหนึ่งสำเนาของค่าสำหรับเธรดทั้งหมดหรือไม่

อย่างไรก็ตามstaticค่าตัวแปรก็จะเป็นหนึ่งค่าสำหรับกระทู้ทั้งหมดแล้วทำไมเราควรจะไปvolatile?


คำอธิบายอย่างเป็นทางการของการระเหย: cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#volatile
Vadzim

คำตอบ:


365

การประกาศตัวแปรแบบคงที่ใน Java หมายความว่าจะมีเพียงสำเนาเดียวไม่ว่าจะสร้างวัตถุจำนวนเท่าใดในชั้นเรียน ตัวแปรจะสามารถเข้าถึงได้แม้จะไม่ได้Objectsสร้างเลยก็ตาม อย่างไรก็ตามเธรดอาจมีค่าแคชในตัวเครื่อง

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

ซึ่งหมายความว่าหากสองเธรดอัปเดตตัวแปรของวัตถุเดียวกันพร้อมกันและตัวแปรไม่ได้ประกาศว่ามีความผันผวนอาจเป็นกรณีที่เธรดตัวใดตัวหนึ่งมีค่าแคชเก่า

แม้ว่าคุณจะเข้าถึงค่าสแตติกผ่านหลายเธรดแต่ละเธรดสามารถมีสำเนาแคชในเครื่อง! เพื่อหลีกเลี่ยงปัญหานี้คุณสามารถประกาศตัวแปรว่าเป็นสารระเหยคงที่และจะบังคับให้เธรดอ่านทุกครั้งที่มีค่าโกลบอล

อย่างไรก็ตามสารระเหยไม่ได้ใช้แทนการซิงโครไนซ์ที่เหมาะสม!
ตัวอย่างเช่น

private static volatile int counter = 0;

private void concurrentMethodWrong() {
  counter = counter + 5;
  //do something
  counter = counter - 5;
}

การดำเนินการconcurrentMethodWrongหลายครั้งพร้อมกันอาจนำไปสู่ค่าสุดท้ายของตัวนับที่แตกต่างจากศูนย์!
ในการแก้ปัญหาคุณต้องใช้การล็อก:

private static final Object counterLock = new Object();

private static volatile int counter = 0;

private void concurrentMethodRight() {
  synchronized (counterLock) {
    counter = counter + 5;
  }
  //do something
  synchronized (counterLock) {
    counter = counter - 5;
  }
}

หรือใช้AtomicIntegerคลาส


7
ตัวดัดแปลงแบบระเหยรับประกันได้ว่าเธรดใด ๆ ที่อ่านฟิลด์จะเห็นค่าที่เขียนล่าสุดดังนั้นจึงจำเป็นถ้าตัวแปรถูกแชร์ระหว่างหลายเธรดและคุณต้องการคุณลักษณะนี้ขึ้นอยู่กับกรณีการใช้งานของคุณ
stivlo

5
แคชคืออะไรเมื่อคุณพูดว่า "แคชในเครื่อง" CPU cache แคช JVM บางประเภท?
mert inan

6
@mertinan ใช่ตัวแปรสามารถอยู่ในแคชใกล้กับโปรเซสเซอร์หรือคอร์ ดูcs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.htmlสำหรับรายละเอียดเพิ่มเติม
stivlo

15
'volatile' ไม่ได้หมายถึง 'หนึ่งตัวแปรต่อวัตถุ' การไม่มี 'สถิต' ทำเช่นนั้น -1 สำหรับความล้มเหลวในการล้างความเข้าใจผิดเบื้องต้นนี้ในส่วนของ OP
มาร์ควิสแห่ง Lorne

27
@EJP ผมคิดว่าประโยคที่ว่า "การประกาศตัวแปรเป็นสารระเหยที่มีจะเป็นหนึ่งในตัวแปรสำหรับแต่ละวัตถุ. ดังนั้นบนพื้นผิวที่ดูเหมือนว่ามีความแตกต่างจากตัวแปรปกติไม่มี" ได้รับการอธิบายว่าผมได้เพิ่มและไม่คงที่ , อย่าลังเลที่จะแก้ไขบทความและปรับปรุงข้อความเพื่อให้ชัดเจนยิ่งขึ้น
stivlo

288

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

ตัวแปรคงที่ : ถ้าสองเธรด (สมมติว่าt1และt2) กำลังเข้าถึงวัตถุเดียวกันและอัปเดตตัวแปรซึ่งถูกประกาศว่าเป็นสแตติกหมายความว่าt1และt2สามารถสร้างสำเนาของวัตถุเดียวกันภายในโลคัล (รวมถึงตัวแปรคงที่) ในแคชของตนดังนั้นอัปเดต ทำโดยt1ตัวแปรคงที่ในแคชท้องถิ่นจะไม่สะท้อนในตัวแปรคงที่สำหรับt2แคช

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

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


6
สวัสดี @Som โปรดแก้ไขฉันหากฉันผิด แต่คุณไม่คิดว่าคำสั่ง " แต่ไม่ได้อยู่ในบริบทของเธรดที่การอัพเดตของเธรดหนึ่งไปยังตัวแปรสแตติกจะสะท้อนการเปลี่ยนแปลงทันทีไปยังเธรดทั้งหมด (ในแคชภายในเครื่อง) " ควรเป็น "แต่ไม่ใช่ในบริบท ของเธรดที่การอัพเดตหนึ่งเธรดเป็นตัวแปรแบบคงที่<<NOT>> จะแสดงการเปลี่ยนแปลงทันทีต่อเธรดทั้งหมด (ในแคชภายในเครื่อง)
Jaikrat

@ Jaikrat ใช่มันทำให้ฉันสับสนมาก ความเข้าใจของฉันคือการที่คุณพูดถูกและคำตอบนี้ผิดตามที่เขียนไว้ ฉันต้องการแก้ไขถ้าฉันผิด
สจ๊วต

@Jaikrat เธรดไม่ได้แคชตัวแปรคงที่ แต่โปรดอ้างถึงตัวแปรคงที่ที่ปรับปรุงแล้ว
Som

@Som แล้วคุณต้องการที่จะแก้ไขพิทักษ์และลบแต่ไม่ได้อยู่ในบริบทของด้าย สับสนมาก ขอบคุณ
Jaikrat

น่าเศร้าคำตอบนี้ไม่ถูกต้อง บน CPUs ที่ทันสมัยแม้กระทั่งvolatileตัวแปรที่สามารถใช้ร่วมกันระหว่างแคชของ CPU ที่แตกต่างกัน สิ่งนี้นำเสนอไม่มีปัญหาเนื่องจากแคชเจรจาต่อรองความเป็นเจ้าของเฉพาะของบรรทัดแคชก่อนที่จะแก้ไข
David Schwartz

32

นอกจากคำตอบอื่น ๆ ฉันต้องการเพิ่มภาพหนึ่งภาพ (รูปทำให้เข้าใจง่าย)

ป้อนคำอธิบายรูปภาพที่นี่

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

volatileประกาศทำให้แน่ใจว่ากระทู้จะไม่แคชข้อมูลและใช้สำเนาที่ใช้ร่วมกันเท่านั้น

แหล่งที่มาของภาพ


1
ตัวแปรสแตติกใช้ร่วมกันระหว่างวัตถุภายใต้เธรดหรือไม่ สิ่งนี้ควรอ่านตัวแปรสแตติกที่ใช้ร่วมกันระหว่างวัตถุวัตถุทั้งหมดโดยไม่คำนึงถึงหัวข้อ
cquezel

1
"ตัวแปรที่ระเหยได้ถูกแบ่งปันในหลาย ๆ เธรด (ดังนั้นวัตถุก็เช่นกัน)" ความผันผวนไม่ได้เปลี่ยนวิธีการใช้ตัวแปรร่วมกันในหลายหัวข้อหรือวัตถุ มันเปลี่ยนวิธีการอนุญาตให้ใช้งานจริงเพื่อแคชค่า
cquezel

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

4
ฉันสับสนมาก ภาพนี้เคลียร์คำถามทั้งหมดของฉัน!
vins

5

ฉันคิดstaticและvolatileไม่มีความสัมพันธ์เลย ฉันขอแนะนำให้คุณอ่านบทช่วยสอนจาวาเพื่อทำความเข้าใจAtomic Accessและทำไมต้องใช้การเข้าถึงของ Atomic เข้าใจว่าInterleavedคืออะไรคุณจะพบคำตอบ


4

ในแง่ง่าย ๆ

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

  2. volatile : คำหลักนี้ใช้ได้กับทั้งตัวแปรclassและinstance

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

มีลักษณะที่นี้บทความโดยJavin Paul จะเข้าใจตัวแปรที่ผันผวนในทางที่ดี

ป้อนคำอธิบายรูปภาพที่นี่

หากไม่มีvolatileคำหลักค่าของตัวแปรในสแต็กของแต่ละเธรดอาจแตกต่างกัน โดยการทำให้ตัวแปรเป็นvolatileกระทู้ทั้งหมดจะได้รับค่าเดียวกันในหน่วยความจำการทำงานและข้อผิดพลาดความสอดคล้องหน่วยความจำของพวกเขาได้รับการหลีกเลี่ยง

คำvariableนี้สามารถเป็นได้ทั้งstaticตัวแปร (คลาส) หรือinstance(วัตถุ)

เกี่ยวกับคำค้นหาของคุณ:

อย่างไรก็ตามค่าตัวแปรแบบคงที่ก็จะเป็นหนึ่งค่าสำหรับกระทู้ทั้งหมดแล้วทำไมเราควรระเหย

หากฉันต้องการinstanceตัวแปรในแอปพลิเคชันของฉันฉันไม่สามารถใช้staticตัวแปรได้ แม้ในกรณีที่มีstaticตัวแปรความมั่นคงไม่ได้รับประกันเนื่องจาก Thread cache ดังแสดงในแผนภาพ

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

ยิ่งไปกว่านั้นมันยังหมายความว่าเมื่อเธรดอ่านตัวแปรระเหยมันไม่เพียง แต่เห็นการเปลี่ยนแปลงล่าสุดของ volatile แต่ยังมีผลข้างเคียงของรหัสที่ทำให้เกิดการเปลี่ยนแปลง => ข้อผิดพลาดที่สอดคล้องกันของหน่วยความจำยังคงเป็นไปได้ . เพื่อหลีกเลี่ยงผลข้างเคียงคุณต้องใช้ตัวแปรที่ซิงโครไนซ์ แต่มีวิธีแก้ปัญหาที่ดีกว่าในจาวา

การใช้การเข้าถึงตัวแปรอะตอมมิกแบบง่ายมีประสิทธิภาพมากกว่าการเข้าถึงตัวแปรเหล่านี้ผ่านรหัสที่ซิงโครไนซ์

คลาสบางส่วนในjava.util.concurrentแพ็คเกจมีวิธีอะตอมมิกที่ไม่ต้องอาศัยการซิงโครไนซ์

อ้างถึงบทความการควบคุมภาวะพร้อมกันระดับสูงนี้สำหรับรายละเอียดเพิ่มเติม

โดยเฉพาะอย่างยิ่งมีลักษณะที่ตัวแปรปรมาณู

คำถาม SE ที่เกี่ยวข้อง:

Volatile Vs Atomic

บูลีนระเหยกับ AtomicBoolean

ความแตกต่างระหว่างสารระเหยและซิงโครไนซ์ใน Java


ฉันซาบซึ้งกับคำตอบนี้จริงๆ ฉันรู้ว่าvolatileก่อนหน้านี้คืออะไรแต่คำตอบนี้ทำให้ฉันชัดเจนมากว่าทำไมฉันยังต้องใช้volatileกับstaticตัวแปร
Chaklader Asfak Arefe

volatile: คำหลักนี้ใช้ได้กับทั้งตัวแปร class และ instance ข้อความที่คุณกล่าวข้างต้นไม่ถูกต้องเกี่ยวกับชั้นเรียน คำสำคัญสองคำเท่านั้นที่สามารถใช้กับตัวแปรนั้นมีความผันผวนและชั่วคราว ความผันผวนจึงไม่สามารถใช้ได้กับคลาส
ASR

สารระเหยใช้ได้กับตัวแปรคลาส (คงที่) ลองดูลิงก์ Singleton single lock ใน google และคุณจะพบว่าความเข้าใจของคุณผิด stackoverflow.com/questions/18093735/…
Ravindra babu

volatile คงที่ส่วนตัวเป็นการประกาศที่ถูกต้อง
Ravindra babu

0

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

ตอนนี้ถ้าใช้ตัวแปรแบบคงที่ในสภาพแวดล้อมแบบมัลติเธรดก็จะมีปัญหาหากคาดว่าจะได้ผลลัพธ์ที่ต้องการ เนื่องจากแต่ละเธรดมีสำเนาของตนเองดังนั้นการเพิ่มหรือลดลงของตัวแปรสแตติกจากเธรดหนึ่งอาจไม่สะท้อนในเธรดอื่น

หากคาดว่าผลลัพธ์ที่ต้องการจากตัวแปรคงที่ให้ใช้ความผันผวนกับคงที่ในหลายเธรดแล้วทุกอย่างจะได้รับการแก้ไข


0

ไม่แน่ใจว่าตัวแปรคงที่จะถูกแคชในหน่วยความจำภายในเธรดหรือไม่ แต่เมื่อฉันรันสองเธรด (T1, T2) ที่เข้าถึงออบเจ็กต์เดียวกัน (obj) และเมื่อการอัปเดตที่ทำโดยเธรด T1 ไปยังตัวแปรสแตติกจะมีผลใน T2


-2

หากเราประกาศตัวแปรเป็นแบบคงที่จะมีเพียงหนึ่งสำเนาของตัวแปร ดังนั้นเมื่อใดก็ตามที่เธรดที่แตกต่างเข้าถึงตัวแปรนั้นจะมีเพียงค่าสุดท้ายสำหรับตัวแปร (เนื่องจากมีหน่วยความจำตำแหน่งเดียวเท่านั้นที่จัดสรรให้กับตัวแปร)

หากตัวแปรถูกประกาศว่าเป็นสารระเหยเธรดทั้งหมดจะมีสำเนาของตัวแปรของตัวเอง แต่ค่าจะถูกนำมาจากหน่วยความจำหลักดังนั้นค่าของตัวแปรในเธรดทั้งหมดจะเหมือนกัน

ดังนั้นในทั้งสองกรณีประเด็นหลักคือค่าของตัวแปรเหมือนกันในทุกกระทู้


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