“สวัสดี อามีโก้!”

“สวัสดี เอลลี่!”

"ฉันอยากจะบอกคุณเกี่ยวกับตัวดัดแปลงที่ระเหยได้ คุณรู้หรือไม่ว่ามันคืออะไร"

"เกี่ยวอะไรกับด้าย ฉันจำไม่ได้แน่ชัด"

"จากนั้นฟัง นี่คือรายละเอียดทางเทคนิคสำหรับคุณ:"

"คอมพิวเตอร์มีหน่วยความจำสองประเภท: หน่วยความจำส่วนกลาง (ปกติ) และหน่วยความจำในตัวโปรเซสเซอร์ หน่วยความจำในตัวโปรเซสเซอร์แบ่งออกเป็นรีจิสเตอร์ แคชระดับแรก (L1) แคชระดับที่สอง (L2) และ ระดับสาม (L3)"

"หน่วยความจำประเภทนี้มีความเร็วต่างกัน หน่วยความจำที่เร็วและเล็กที่สุดคือรีจิสเตอร์ จากนั้นแคชโปรเซสเซอร์ (L1, L2, L3) และสุดท้ายคือหน่วยความจำส่วนกลาง (ช้าที่สุด)"

"หน่วยความจำส่วนกลางและแคชของโปรเซสเซอร์ทำงานที่ความเร็วต่างกันอย่างมาก ดังนั้นเครื่อง Java จึงอนุญาตให้แต่ละเธรดจัดเก็บตัวแปรที่ใช้บ่อยที่สุดในหน่วยความจำเธรดภายในเครื่อง (ในแคชของโปรเซสเซอร์)"

"กระบวนการนี้สามารถควบคุมได้หรือไม่"

"ไม่จริง งานทั้งหมดทำโดยเครื่อง Java มันฉลาดมากเมื่อพูดถึงการปรับประสิทธิภาพให้เหมาะสม"

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

“อืม แล้วจะทำอะไรได้ล่ะ”

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

public volatile int count = 0;

“อ๋อ ฉันจำได้ เธอเคยพูดเรื่องนี้แล้ว ฉันรู้เรื่องนี้แล้ว”

“แน่ใจ แต่เธอจำได้ก็ต่อเมื่อฉันบอกคุณเท่านั้น”

“เอ่อ ลืมไปนิดนึง”

"การทำซ้ำคือแม่ของการเรียนรู้!"

"ต่อไปนี้เป็นข้อเท็จจริงใหม่บางประการเกี่ยวกับตัวดัดแปลงที่ระเหยได้ ตัวดัดแปลงที่ระเหยได้รับประกันว่าตัวแปรจะถูกอ่านและเขียนอย่างปลอดภัยเท่านั้น ไม่ได้รับประกันว่าจะเปลี่ยนแปลงได้อย่างปลอดภัย"

"อะไรคือความแตกต่าง?"

"ดูว่าตัวแปรมีการเปลี่ยนแปลงอย่างไร:"

รหัส เกิดอะไรขึ้นจริง: คำอธิบาย
count++
register = count;

register = register+1;

count = register;
ขั้นตอนที่ 1
ค่าจำนวนตัวแปรถูกคัดลอกจากหน่วยความจำส่วนกลางไปยังรีจิสเตอร์ตัวประมวลผล

ขั้นตอนที่ 2
ภายในโปรเซสเซอร์ ตัวแปรรีจิสเตอร์จะเพิ่มขึ้น 1

ขั้นตอนที่ 3
ค่าของตัวแปรถูกคัดลอกจากโปรเซสเซอร์ไปยังหน่วยความจำส่วนกลาง

"ว้าว! ตัวแปรทั้งหมดถูกเปลี่ยนในโปรเซสเซอร์เท่านั้นเหรอ?"

"ใช่."

"และค่าจะถูกคัดลอกไปมา: จากหน่วยความจำไปยังโปรเซสเซอร์และย้อนกลับ"

"ใช่."

"โมดิฟายเออร์ระเหยรับประกันว่าเมื่อมีการเข้าถึงจำนวนตัวแปร มันจะถูกอ่านจากหน่วยความจำ (ขั้นตอนที่ 1) และถ้าเธรดต้องการกำหนดค่าใหม่ มันจะอยู่ในหน่วยความจำส่วนกลางอย่างแน่นอน (ขั้นตอนที่ 3)"

"แต่เครื่อง Java ไม่รับประกันว่าจะไม่มีการสลับเธรดระหว่างขั้นตอนที่ 1 และ 3"

"ดังนั้น การเพิ่มตัวแปรทีละ 1 จึงเป็นการดำเนินการ 3 รายการจริงหรือ"

"ใช่."

"และหากเธรดสองเธรดต้องการดำเนินการ count++ พร้อมกัน เธรดเหล่านั้นอาจรบกวนซึ่งกันและกันได้"

"ใช่ ลองดูสิ:"

หัวข้อที่ 1 หัวข้อที่ 2 ผลลัพธ์
register1 = count;
register1++;
count = register1;
register2 = count;
register2++;
count = register2;
register1 = count;
register2 = count;
register2++;
count = register2;
register1++;
count = register1;

"คุณสามารถเข้าถึงตัวแปรได้ แต่การเปลี่ยนแปลงยังคงมีความเสี่ยงอยู่ใช่ไหม"

"ก็เปลี่ยนได้นะ ระวังหน่อย ☺"

"ยังไง?"

" ซิงโครไนซ์  คือเพื่อนที่ดีที่สุดของเรา"

"ฉันเห็น."