0Pricing
Java Academy · บทเรียน

ตัวแปร Atomic: การอัปเดตแบบไร้ล็อก

ใช้ AtomicInteger, AtomicLong และ AtomicReference สำหรับตัวนับที่ปลอดภัยต่อเธรดโดยไม่ใช้ล็อก

ตัวแปร Atomic: การอัปเดตแบบไร้ล็อก เป็นบทเรียน Java Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Java Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน

ปัญหาของการดำเนินการที่ไม่เป็นอะตอมิก

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

// UNSAFE:
int count = 0;
// Thread A reads count=0, Thread B reads count=0
// Both write 1 — second update lost!
count++; // NOT thread-safe

// SAFE:
import java.util.concurrent.atomic.*;
AtomicInteger atomicCount = new AtomicInteger(0);
atomicCount.incrementAndGet(); // atomic, lock-free

พื้นฐานของ AtomicInteger

AtomicInteger มีการดำเนินการกับจำนวนเต็มที่รับประกันความเป็นอะตอมิก โดยใช้คำสั่ง compare-and-swap (CAS) ของ CPU — ไม่จำเป็นต้องใช้ล็อก

AtomicInteger counter = new AtomicInteger(0);

counter.set(10);                   // set to 10
System.out.println(counter.get()); // 10

int prev = counter.getAndIncrement(); // returns 10, sets to 11
int curr = counter.incrementAndGet(); // sets to 12, returns 12
counter.addAndGet(5);                 // adds 5, returns 17
counter.decrementAndGet();            // subtracts 1, returns 16

compareAndSet: การดำเนินการ CAS

compareAndSet(expected, update) จะอัปเดตค่าอย่างเป็นอะตอมิกก็ต่อเมื่อค่าปัจจุบันเท่ากับค่าที่คาดไว้เท่านั้น คืนค่าเป็นจริงหากสำเร็จ นี่คือพื้นฐานของอัลกอริทึมทั้งหมดที่ไม่ใช้ล็อก

AtomicInteger ai = new AtomicInteger(5);

boolean updated = ai.compareAndSet(5, 10); // current==5? set to 10
System.out.println(updated); // true
System.out.println(ai.get()); // 10

boolean failed = ai.compareAndSet(5, 20); // current==10, not 5 → fails
System.out.println(failed); // false
System.out.println(ai.get()); // still 10

AtomicLong สำหรับตัวนับ

AtomicLong เป็นชนิดเทียบเท่าขนาด 64 บิต ซึ่งมักใช้กับตัวสร้างลำดับและตัวนับจำนวนครั้งที่พบ:

AtomicLong seq = new AtomicLong(0);

// Thread-safe sequence generator:
long nextId() {
    return seq.incrementAndGet();
}

System.out.println(nextId()); // 1
System.out.println(nextId()); // 2
System.out.println(nextId()); // 3

AtomicBoolean

แฟล็กบูลีนที่ปลอดภัยต่อเธรด มักใช้สำหรับการเริ่มต้นเพียงครั้งเดียวหรือสัญญาณการปิดระบบ:

AtomicBoolean initialized = new AtomicBoolean(false);

void initOnce() {
    // compareAndSet: only first caller succeeds
    if (initialized.compareAndSet(false, true)) {
        System.out.println("Initializing...");
        // perform expensive init
    } else {
        System.out.println("Already initialized");
    }
}

AtomicReference

AtomicReference<V> ให้อัปเดตการอ้างอิงอ็อบเจ็กต์ได้อย่างเป็นอะตอมิก ใช้สำหรับสร้างโครงสร้างข้อมูลที่ไม่ใช้ล็อก:

import java.util.concurrent.atomic.*;

record Config(String host, int port) {}

AtomicReference<Config> configRef =
    new AtomicReference<>(new Config("localhost", 8080));

// Hot-swap the configuration atomically:
Config oldConfig = configRef.get();
Config newConfig = new Config("prod.example.com", 443);

boolean swapped = configRef.compareAndSet(oldConfig, newConfig);
System.out.println(swapped); // true
System.out.println(configRef.get()); // Config[host=prod.example.com, port=443]

updateAndGet / getAndUpdate

ใช้ฟังก์ชันกับค่าอย่างเป็นอะตอมิก โดยภายในใช้ลูปลอง CAS ใหม่:

AtomicInteger value = new AtomicInteger(10);

// Apply function atomically: square the value
int newVal = value.updateAndGet(x -> x * x);
System.out.println(newVal); // 100

// Get old, then apply:
int old = value.getAndUpdate(x -> x + 1);
System.out.println(old);   // 100
System.out.println(value.get()); // 101

accumulateAndGet

รวมค่าปัจจุบันกับค่าที่กำหนดโดยใช้ฟังก์ชัน:

AtomicInteger max = new AtomicInteger(0);

// Keep running maximum:
max.accumulateAndGet(42, Math::max); // max(0, 42) = 42
max.accumulateAndGet(15, Math::max); // max(42, 15) = 42
max.accumulateAndGet(99, Math::max); // max(42, 99) = 99

System.out.println(max.get()); // 99

LongAdder สำหรับตัวนับที่มีการแย่งทรัพยากรสูง

เมื่อมีการแย่งทรัพยากรระหว่างเธรดสูง การลอง CAS ใหม่ของ AtomicLong อาจทำให้ประสิทธิภาพลดลง LongAdder ใช้เซลล์หลายตัวเพื่อลดการแย่งทรัพยากร:

import java.util.concurrent.atomic.*;

LongAdder adder = new LongAdder();

// Multiple threads can call add() with minimal contention:
adder.increment();
adder.add(5);
adder.increment();

System.out.println(adder.sum()); // 7
// Note: sum() is NOT atomic with add() — use only when no concurrent adds

อาร์เรย์แบบอะตอมิก

AtomicIntegerArray / AtomicLongArray มีการดำเนินการแบบอะตอมิกกับสมาชิกแต่ละตัวของอาร์เรย์:

AtomicIntegerArray arr = new AtomicIntegerArray(5);

arr.set(0, 10);
arr.incrementAndGet(0);
arr.compareAndSet(2, 0, 42); // index 2: if 0, set 42

System.out.println(arr.get(0)); // 11
System.out.println(arr.get(2)); // 42

ควรใช้อะตอมิกหรือล็อกเมื่อใด

ใช้ตัวแปรแบบอะตอมิกเมื่อ:

  • อัปเดตตัวแปรเดียว เช่น ตัวนับ แฟล็ก และการอ้างอิง
  • มีการแย่งทรัพยากรต่ำ
  • ประสิทธิภาพแบบไม่ใช้ล็อกมีความสำคัญ

ใช้ล็อกเมื่อ:

  • ต้องอัปเดตตัวแปรหลายตัวอย่างเป็นอะตอมิก
  • ต้องใช้ตรรกะแบบมีเงื่อนไขที่ซับซ้อน
  • มีการแย่งทรัพยากรสูงมาก (การลอง CAS ใหม่ทำให้ประสิทธิภาพลดลง)

ตรวจสอบอย่างรวดเร็ว

compareAndSet(expected, update) รับประกันอะไร

สรุป: ตัวแปรแบบอะตอมิก

ประเด็นสำคัญ:

  • ชนิดอะตอมิกใช้คำสั่ง CAS ของ CPU — ไม่ใช้ล็อกและปลอดภัยต่อเธรด
  • AtomicInteger/Long/Boolean/Reference ใช้เพื่อความเป็นอะตอมิกของตัวแปรเดียว
  • compareAndSet เป็นพื้นฐานของอัลกอริทึมที่ไม่ใช้ล็อก
  • ใช้ updateAndGet/accumulateAndGet สำหรับการอัปเดตด้วยฟังก์ชัน
  • ใช้ LongAdder ในกรณีตัวนับที่มีการแย่งทรัพยากรสูง

คำถามที่พบบ่อย

บทเรียน “ตัวแปร Atomic: การอัปเดตแบบไร้ล็อก” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “ตัวแปร Atomic: การอัปเดตแบบไร้ล็อก” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Java Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “ตัวแปร Atomic: การอัปเดตแบบไร้ล็อก”

ใช้ AtomicInteger, AtomicLong และ AtomicReference สำหรับตัวนับที่ปลอดภัยต่อเธรดโดยไม่ใช้ล็อก คุณปฏิบัติ Java Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Java Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Java Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน

บทเรียน “ตัวแปร Atomic: การอัปเดตแบบไร้ล็อก” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Java Academy นี้ได้ไหม

ได้ บทเรียน Java Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. ReentrantLock กับ synchronized
  2. ReadWriteLock สำหรับสถานการณ์ผู้อ่าน-ผู้เขียน
  3. ตัวแปร Atomic: การอัปเดตแบบไร้ล็อก
  4. StampedLock และการอ่านเชิงคาดคะเน
← กลับไปที่ Java Academy