Memory Consistency Error คืออะไร?
เมื่อ thread หลายตัวเข้าถึง shared data การเปลี่ยนแปลงของ thread หนึ่งอาจ ไม่มองเห็น โดย thread อีกตัว ทำให้มุมมองข้อมูลไม่สอดคล้อง (inconsistent view)
สาเหตุหลัก
- CPU Cache: แต่ละ core มี cache (L1/L2) ของตัวเอง เขียนค่าใหม่อาจอยู่ใน cache ยังไม่ flush ไป main memory
- Reordering: Compiler/CPU จัดลำดับคำสั่งใหม่เพื่อประสิทธิภาพ (instruction reordering)
- Non-atomic operations: การอ่าน/เขียน 64-bit (long/double) บนบาง architecture ไม่ใช่ atomic
ตัวอย่าง: Race Condition + Visibility Problem
class Shared {
static int counter = 0;
void increment() {
for (int i = 0; i < 5; i++) {
counter++;
System.out.println("Inc: " + counter);
}
}
void decrement() {
for (int i = 0; i < 5; i++) {
counter--;
System.out.println("Dec: " + counter);
}
}
}
public class Demo {
public static void main(String[] args) {
Shared shared = new Shared();
Thread t1 = new Thread(shared::increment);
Thread t2 = new Thread(shared::decrement);
t1.start();
t2.start();
}
}
ผลลัพธ์ (ไม่คงที่ - race condition)
Inc: 1 Dec: 0 Dec: -1 Inc: 0 Inc: 1 Dec: 0 Inc: 2 Dec: 1 Dec: 0 Inc: 1
วิธีแก้: Happens-Before Relationship
สร้าง happens-before เพื่อรับประกัน visibility:
Thread.start()→ happens-before ทุก action ใน thread ใหม่Thread.join()→ happens-before หลัง thread จบvolatilevariable: write happens-before readsynchronized: unlock happens-before lockAtomicInteger,ConcurrentHashMapetc.
แก้ตัวอย่างด้วย volatile
class Shared {
volatile static int counter = 0; // volatile!
// ... methods ...
}
หมายเหตุ: volatile แก้ visibility แต่ ไม่แก้ atomicity ของ counter++ (read-modify-write) ต้องใช้ AtomicInteger หรือ synchronized ร่วมด้วย