มันถูกต้องหรือไม่ที่จะบอกว่านั่นstatic
หมายถึงหนึ่งสำเนาของค่าสำหรับวัตถุทั้งหมดและvolatile
หมายถึงหนึ่งสำเนาของค่าสำหรับเธรดทั้งหมดหรือไม่
อย่างไรก็ตามstatic
ค่าตัวแปรก็จะเป็นหนึ่งค่าสำหรับกระทู้ทั้งหมดแล้วทำไมเราควรจะไปvolatile
?
มันถูกต้องหรือไม่ที่จะบอกว่านั่นstatic
หมายถึงหนึ่งสำเนาของค่าสำหรับวัตถุทั้งหมดและvolatile
หมายถึงหนึ่งสำเนาของค่าสำหรับเธรดทั้งหมดหรือไม่
อย่างไรก็ตามstatic
ค่าตัวแปรก็จะเป็นหนึ่งค่าสำหรับกระทู้ทั้งหมดแล้วทำไมเราควรจะไปvolatile
?
คำตอบ:
การประกาศตัวแปรแบบคงที่ใน 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
คลาส
ความแตกต่างระหว่างคงที่และระเหย:
ตัวแปรคงที่ : ถ้าสองเธรด (สมมติว่าt1
และt2
) กำลังเข้าถึงวัตถุเดียวกันและอัปเดตตัวแปรซึ่งถูกประกาศว่าเป็นสแตติกหมายความว่าt1
และt2
สามารถสร้างสำเนาของวัตถุเดียวกันภายในโลคัล (รวมถึงตัวแปรคงที่) ในแคชของตนดังนั้นอัปเดต ทำโดยt1
ตัวแปรคงที่ในแคชท้องถิ่นจะไม่สะท้อนในตัวแปรคงที่สำหรับt2
แคช
ตัวแปรแบบสแตติกถูกใช้ในบริบทของวัตถุซึ่งการอัพเดทที่ทำโดยวัตถุหนึ่งจะสะท้อนให้เห็นในวัตถุอื่น ๆ ทั้งหมดของคลาสเดียวกันแต่ไม่ได้อยู่ในบริบทของเธรดที่การอัพเดตของเธรดหนึ่งไปยังตัวแปรแบบคงที่จะสะท้อนถึงการเปลี่ยนแปลงทันทีทั้งหมด หัวข้อ (ในแคชท้องถิ่นของพวกเขา)
ตัวแปรระเหย : ถ้าสองกระทู้ (สมมติt1
และt2
) มีการเข้าถึงวัตถุเดียวกันและปรับปรุงตัวแปรที่ถูกประกาศเป็นสารระเหยแล้วมันหมายถึงt1
และt2
สามารถทำให้แคชในท้องถิ่นของตัวเองของวัตถุยกเว้นตัวแปรที่มีการประกาศเป็นสารระเหย ดังนั้นตัวแปรระเหยจะมีเพียงหนึ่งสำเนาหลักซึ่งจะถูกอัพเดตโดยเธรดที่แตกต่างกันและอัพเดตที่ทำโดยเธรดหนึ่งไปยังตัวแปรระเหยจะสะท้อนไปยังเธรดอื่นทันที
volatile
ตัวแปรที่สามารถใช้ร่วมกันระหว่างแคชของ CPU ที่แตกต่างกัน สิ่งนี้นำเสนอไม่มีปัญหาเนื่องจากแคชเจรจาต่อรองความเป็นเจ้าของเฉพาะของบรรทัดแคชก่อนที่จะแก้ไข
นอกจากคำตอบอื่น ๆ ฉันต้องการเพิ่มภาพหนึ่งภาพ (รูปทำให้เข้าใจง่าย)
static
ตัวแปรอาจถูกแคชสำหรับแต่ละเธรด ในสภาพแวดล้อมแบบมัลติเธรดหากหนึ่งเธรดแก้ไขข้อมูลแคชซึ่งอาจไม่สะท้อนเธรดอื่นเนื่องจากมีสำเนาอยู่
volatile
ประกาศทำให้แน่ใจว่ากระทู้จะไม่แคชข้อมูลและใช้สำเนาที่ใช้ร่วมกันเท่านั้น
ฉันคิดstatic
และvolatile
ไม่มีความสัมพันธ์เลย ฉันขอแนะนำให้คุณอ่านบทช่วยสอนจาวาเพื่อทำความเข้าใจAtomic Accessและทำไมต้องใช้การเข้าถึงของ Atomic เข้าใจว่าInterleavedคืออะไรคุณจะพบคำตอบ
ในแง่ง่าย ๆ
คง : static
ตัวแปรที่เกี่ยวข้องกับระดับมากกว่ากับวัตถุ ทุกอินสแตนซ์ของคลาสใช้ตัวแปรคลาสร่วมกันซึ่งอยู่ในที่เดียวในหน่วยความจำ
volatile : คำหลักนี้ใช้ได้กับทั้งตัวแปรclassและinstance
การใช้ตัวแปรระเหยช่วยลดความเสี่ยงของข้อผิดพลาดความสอดคล้องของหน่วยความจำเนื่องจากการเขียนใด ๆ ไปยังตัวแปรระเหยสร้างความสัมพันธ์ที่เกิดขึ้นก่อนที่จะมีการอ่านตัวแปรเดียวกันที่ตามมา ซึ่งหมายความว่าการเปลี่ยนแปลงของตัวแปรที่เปลี่ยนแปลงได้จะเกิดขึ้นกับเธรดอื่น ๆ
มีลักษณะที่นี้บทความโดยJavin Paul
จะเข้าใจตัวแปรที่ผันผวนในทางที่ดี
หากไม่มีvolatile
คำหลักค่าของตัวแปรในสแต็กของแต่ละเธรดอาจแตกต่างกัน โดยการทำให้ตัวแปรเป็นvolatile
กระทู้ทั้งหมดจะได้รับค่าเดียวกันในหน่วยความจำการทำงานและข้อผิดพลาดความสอดคล้องหน่วยความจำของพวกเขาได้รับการหลีกเลี่ยง
คำvariable
นี้สามารถเป็นได้ทั้งstatic
ตัวแปร (คลาส) หรือinstance
(วัตถุ)
เกี่ยวกับคำค้นหาของคุณ:
อย่างไรก็ตามค่าตัวแปรแบบคงที่ก็จะเป็นหนึ่งค่าสำหรับกระทู้ทั้งหมดแล้วทำไมเราควรระเหย
หากฉันต้องการinstance
ตัวแปรในแอปพลิเคชันของฉันฉันไม่สามารถใช้static
ตัวแปรได้ แม้ในกรณีที่มีstatic
ตัวแปรความมั่นคงไม่ได้รับประกันเนื่องจาก Thread cache ดังแสดงในแผนภาพ
การใช้volatile
ตัวแปรช่วยลดความเสี่ยงของข้อผิดพลาดความสอดคล้องของหน่วยความจำเนื่องจากการเขียนใด ๆ ไปยังตัวแปรระเหยสร้างความสัมพันธ์ที่เกิดขึ้นก่อนที่จะมีการอ่านตัวแปรเดียวกันที่ตามมา ซึ่งหมายความว่าการเปลี่ยนแปลงตัวแปรผันแปรจะสามารถมองเห็นได้ในหัวข้ออื่น ๆ
ยิ่งไปกว่านั้นมันยังหมายความว่าเมื่อเธรดอ่านตัวแปรระเหยมันไม่เพียง แต่เห็นการเปลี่ยนแปลงล่าสุดของ volatile แต่ยังมีผลข้างเคียงของรหัสที่ทำให้เกิดการเปลี่ยนแปลง => ข้อผิดพลาดที่สอดคล้องกันของหน่วยความจำยังคงเป็นไปได้ . เพื่อหลีกเลี่ยงผลข้างเคียงคุณต้องใช้ตัวแปรที่ซิงโครไนซ์ แต่มีวิธีแก้ปัญหาที่ดีกว่าในจาวา
การใช้การเข้าถึงตัวแปรอะตอมมิกแบบง่ายมีประสิทธิภาพมากกว่าการเข้าถึงตัวแปรเหล่านี้ผ่านรหัสที่ซิงโครไนซ์
คลาสบางส่วนในjava.util.concurrent
แพ็คเกจมีวิธีอะตอมมิกที่ไม่ต้องอาศัยการซิงโครไนซ์
อ้างถึงบทความการควบคุมภาวะพร้อมกันระดับสูงนี้สำหรับรายละเอียดเพิ่มเติม
โดยเฉพาะอย่างยิ่งมีลักษณะที่ตัวแปรปรมาณู
คำถาม SE ที่เกี่ยวข้อง:
volatile
ก่อนหน้านี้คืออะไรแต่คำตอบนี้ทำให้ฉันชัดเจนมากว่าทำไมฉันยังต้องใช้volatile
กับstatic
ตัวแปร
การเข้าถึงค่าตัวแปรที่ผันผวนจะถูกส่งโดยตรงจากหน่วยความจำหลัก ควรใช้ในสภาพแวดล้อมแบบมัลติเธรดเท่านั้น ตัวแปรคงที่จะถูกโหลดหนึ่งครั้ง หากถูกใช้ในสภาพแวดล้อมเธรดเดียวแม้ว่าสำเนาของตัวแปรจะถูกอัพเดตและจะไม่มีอันตรายใด ๆ ในการเข้าถึงเนื่องจากมีเพียงเธรดเดียวเท่านั้น
ตอนนี้ถ้าใช้ตัวแปรแบบคงที่ในสภาพแวดล้อมแบบมัลติเธรดก็จะมีปัญหาหากคาดว่าจะได้ผลลัพธ์ที่ต้องการ เนื่องจากแต่ละเธรดมีสำเนาของตนเองดังนั้นการเพิ่มหรือลดลงของตัวแปรสแตติกจากเธรดหนึ่งอาจไม่สะท้อนในเธรดอื่น
หากคาดว่าผลลัพธ์ที่ต้องการจากตัวแปรคงที่ให้ใช้ความผันผวนกับคงที่ในหลายเธรดแล้วทุกอย่างจะได้รับการแก้ไข
ไม่แน่ใจว่าตัวแปรคงที่จะถูกแคชในหน่วยความจำภายในเธรดหรือไม่ แต่เมื่อฉันรันสองเธรด (T1, T2) ที่เข้าถึงออบเจ็กต์เดียวกัน (obj) และเมื่อการอัปเดตที่ทำโดยเธรด T1 ไปยังตัวแปรสแตติกจะมีผลใน T2
หากเราประกาศตัวแปรเป็นแบบคงที่จะมีเพียงหนึ่งสำเนาของตัวแปร ดังนั้นเมื่อใดก็ตามที่เธรดที่แตกต่างเข้าถึงตัวแปรนั้นจะมีเพียงค่าสุดท้ายสำหรับตัวแปร (เนื่องจากมีหน่วยความจำตำแหน่งเดียวเท่านั้นที่จัดสรรให้กับตัวแปร)
หากตัวแปรถูกประกาศว่าเป็นสารระเหยเธรดทั้งหมดจะมีสำเนาของตัวแปรของตัวเอง แต่ค่าจะถูกนำมาจากหน่วยความจำหลักดังนั้นค่าของตัวแปรในเธรดทั้งหมดจะเหมือนกัน
ดังนั้นในทั้งสองกรณีประเด็นหลักคือค่าของตัวแปรเหมือนกันในทุกกระทู้