เพื่อนหลายคนอาจเคยได้ยินคำหลักที่ผันผวนและอาจใช้มัน ก่อนหน้า Java 5 มันเป็นคำหลักที่ขัดแย้งกันเนื่องจากการใช้ในโปรแกรมมักจะส่งผลให้ผลลัพธ์ที่ไม่คาดคิด หลังจาก Java 5 คำหลักที่ผันผวนได้กลับมามีชีวิตชีวาอีกครั้ง แม้ว่าคำหลักที่ผันผวนจะเข้าใจง่าย แต่ก็ไม่ง่ายเลยที่จะใช้งานได้ดี
1. คำนำ
JMM ให้คำจำกัดความตัวแปรผันผวน, บล็อกสุดท้าย, ซิงโครไนซ์บล็อกเพื่อให้แน่ใจว่าการมองเห็น
สำหรับตัวแปรที่แก้ไขด้วยความผันผวนเธรดจะอ่านค่าที่แก้ไขมากที่สุดของตัวแปรทุกครั้งที่ใช้ตัวแปร ความผันผวนจะถูกนำไปใช้ในทางที่ผิดและใช้สำหรับการดำเนินการอะตอม ฉันได้เขียนตัวอย่างการทดสอบสองสามครั้งคุณสามารถลองได้
2. โปรแกรมหลัก
คลาสสาธารณะหลัก {โมฆะสาธารณะคงที่หลัก (สตริง [] args) พ่น InterruptedException {รายการ <stread> ThreadList = arrayList ใหม่ <stread> (); สำหรับ (int i = 0; i <10; ++ i) {เธรด = เธรดใหม่ (ใหม่ runnable () {single.holder.instance.add ();}}); threadlist.add (เธรด); thread.start ();} สำหรับ (เธรดเธรด: ThreadList) thread.oin (); System.out.println (single.holder.instance.x);}}}}}}}}}}3. การทดสอบโหมด Singleton
1. ไม่มีความผันผวนไม่มีการซิงโครไนซ์
คลาส single {public int x = 0; โมฆะสาธารณะเพิ่ม () {ลอง {timeunit.milliseconds.sleep (50);} catch (interruptedException e) {E.printstackTrace ();} ++ this.x;} เจ้าของชั้นเรียนคงที่สาธารณะผลลัพธ์ผลลัพธ์: 8, 9 และ 10 มีทั้งหมดปรากฏขึ้น คุณสามารถทำงานได้มากขึ้นและลองมากขึ้นและคุณจะพบผลลัพธ์ที่แตกต่างกัน
2. มีความผันผวน แต่ไม่มีการซิงโครไนซ์
คลาส single {public ระเหยได้ int x = 0; โมฆะสาธารณะเพิ่ม () {ลอง {timeunit.milliseconds.sleep (50);} catch (interruptedException e) {E.printstackTrace ();} ++ this.x;} เจ้าของชั้นเรียนคงที่สาธารณะผลลัพธ์ผลลัพธ์: จำนวนสูงสุดของเหตุการณ์คือ 9 และ 10
3. ไม่มีความผันผวน, ซิงโครไนซ์
คลาสเดี่ยว {public int x = 0; โมฆะแบบซิงโครไนซ์สาธารณะเพิ่ม () {ลอง {timeUnit.milliseconds.sleep (50);} catch (interruptedException e) {E.printstacktrace ();} ++ this.x;ผลลัพธ์ผลลัพธ์: ไม่ว่าคุณจะวิ่งกี่ครั้งมันก็จะเป็น 10
4. เกี่ยวกับแอปพลิเคชันของความผันผวนใน DCL (ตรวจสอบอีกครั้งล็อคล็อค)
Lazysingleton คลาสสาธารณะ {Private Int Simefield; อินสแตนซ์ Lazysingleton แบบคงที่ส่วนตัว; Lazysingleton ส่วนตัว () {this.somefield = new Random () Nextint (200) +1; // (1)} Lazysingletleton สาธารณะ getInstance () {ถ้า (อินสแตนซ์ == null) {// (2) ซิงโครไนซ์ (lazysingleton.class) {// (3) ถ้า (อินสแตนซ์ == null) {// (4) อินสแตนซ์ = ใหม่ lazysingleton (); // (5)}}} ส่งคืนอินสแตนซ์; // (6)} สาธารณะ int getomefield () {return this.somefield; // (7)}}ก่อนอื่นให้ฉันอธิบายว่าทำไมวิธีการเขียนนี้ไม่ทำงานใน Java!
สมมติว่าเธรดฉันกำลังเรียกวิธีการ getInstance () เป็นครั้งแรกจากนั้นเธรด II ยังเรียกวิธีการ getInstance () และวิธีการ getSomefield () สิ่งที่เราต้องการอธิบายคือคำสั่งของเธรด I (1) ไม่ได้เกิดขึ้นก่อนหน้าของเธรด II (7) เมื่อเธรด II เรียกใช้คำสั่ง (2) ของวิธี getInstance () เนื่องจากการเข้าถึงอินสแตนซ์ไม่ได้อยู่ในบล็อกซิงโครนัสเธรด II อาจหรือไม่อาจสังเกตเธรดฉันเขียนถึงอินสแตนซ์ในคำสั่ง (5) นั่นคือค่าของอินสแตนซ์อาจว่างเปล่าหรือไม่ว่างเปล่า ก่อนอื่นเราสมมติว่าค่าของอินสแตนซ์ไม่ว่างดังนั้นเราจึงสังเกตว่าเธรดที่ฉันเขียนอินสแตนซ์ ในเวลานี้เธรด II จะเรียกใช้คำสั่ง (6) และส่งคืนค่าของอินสแตนซ์นี้โดยตรงจากนั้นเรียกใช้เมธอด getSomefield () ในอินสแตนซ์นี้ วิธีนี้เรียกอีกอย่างว่าไม่มีการซิงโครไนซ์ใด ๆ ดังนั้นการทำงานทั้งหมดของเธรด II จึงถูกเรียกโดยไม่มีการซิงโครไนซ์ นี่แสดงให้เห็นว่าไม่มีความสัมพันธ์ที่เกิดขึ้นก่อนหน้านี้ระหว่างคำสั่ง (1) ของเธรด I และคำสั่ง (7) ของเธรด II ซึ่งหมายความว่าเธรด II อาจไม่สามารถสังเกตค่าที่เขียนโดยเธรดฉันไปที่บางคนที่คำสั่ง (1) นี่คือปัญหาของ DCL มันไร้สาระใช่มั้ย DCL เดิมตั้งใจที่จะหลบหนีการซิงโครไนซ์และบรรลุเป้าหมายนี้ มันเป็นเพราะสิ่งนี้อย่างแม่นยำว่ามันถูกลงโทษในที่สุด มีข้อบกพร่องร้ายแรงในโปรแกรมดังกล่าวแม้ว่าความน่าจะเป็นของข้อผิดพลาดที่ถูกค้นพบนั้นต่ำกว่าความน่าจะเป็นที่จะชนะลอตเตอรีและมันก็หายวับไป สิ่งที่น่ากลัวกว่านั้นคือแม้ว่ามันจะเกิดขึ้นคุณจะไม่คิดว่ามันเกิดจาก DCL
ความเข้าใจของฉันคือทั้งเธรด I และ Thread II มีที่เก็บข้อมูลการทำงานของตัวเอง หลังจากเธรดฉันสร้างอินสแตนซ์เวลาในการรีเฟรชเป็นหน่วยความจำไม่แน่นอนดังนั้นจึงเป็นไปได้ทั้งหมดที่เธรด II ไม่สามารถสังเกตค่าที่เขียนโดยเธรดฉันไปที่บางคนที่คำสั่ง (1)
ดังนั้นเนื่องจากมีการเพิ่มกฎเพิ่มเติมก่อนที่จะเพิ่มใน Java 5:
•การดำเนินการเขียนไปยังฟิลด์ที่ผันผวนเกิดขึ้นก่อนการดำเนินการอ่านที่ตามมาในฟิลด์เดียวกัน
การใช้กฎนี้เราสามารถประกาศอินสแตนซ์เป็นความผันผวนได้นั่นคือ: อินสแตนซ์ Lazysingleton ที่ผันผวนส่วนตัว
ตามกฎนี้เราสามารถรับคำสั่งของเธรดฉัน (5) -> ประโยคของเธรด II (2) (นั่นคือเธรด) ตามกฎเธรดเดี่ยวคำสั่งของเธรด i (1) -> ประโยคของเธรด i (5) และประโยคของเธรด II (2) -> ค่าเขียนของเธรด i ถึงบาง filed ในคำสั่ง (1) และโปรแกรมสามารถรับพฤติกรรมที่ถูกต้อง
ภาคผนวก: ก่อน Java5 ไม่มีความแตกต่างระหว่างความหมายแบบซิงโครนัสของฟิลด์สุดท้ายและตัวแปรอื่น ๆ ใน Java5 เมื่อตัวแปรสุดท้ายถูกตั้งค่าในตัวสร้าง (โดยมีเงื่อนไขว่าการอ้างอิงนี้จะไม่รั่วไหลในตัวสร้าง) เธรดอื่น ๆ จะเห็นค่าที่ตั้งไว้ในตัวสร้างอย่างแน่นอน ปัญหาเกี่ยวกับ DCL คือเราเห็นค่าเริ่มต้นของตัวแปรสมาชิกของวัตถุดังนั้นเราจึงสามารถตั้งค่าตัวแปร Somefield ของ Lazysingleton เป็นสุดท้ายเพื่อให้สามารถทำงานได้อย่างถูกต้องใน Java5
เนื้อหาข้างต้นคือความรู้เกี่ยวกับคำหลักที่ผันผวนใน Java แนะนำให้คุณรู้จักโดยบรรณาธิการ ฉันหวังว่ามันจะเป็นประโยชน์กับทุกคน!