Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Java

ความแตกต่างระหว่าง CountDownLatch และ CyclicBarrier ใน Java Concurrency

ทั้งคู่อยู่ใน java.util.concurrent ใช้สำหรับ **synchronization** ระหว่าง threads แต่มีจุดประสงค์และการทำงานต่างกัน

ตารางเปรียบเทียบ

ลำดับหัวข้อCountDownLatchCyclicBarrier
1วัตถุประสงค์รอให้ **ชุดของ tasks** เสร็จสิ้น (one-shot)รอให้ **ชุดของ threads** พบกันที่ barrier point (reusable)
2นับอะไรนับ **tasks** (countDown)นับ **threads** (await)
3Reusable**ไม่ได้** (ใช้ครั้งเดียว count ลงถึง 0 จบ)**ได้** (reset อัตโนมัติหลังทุก barrier)
4Runnable Actionไม่มีมี (constructor รับ Runnable รันเมื่อทุก thread มาถึง)
5Exception HandlingThread ที่ interrupt ได้ InterruptedException เดียวThread หนึ่ง interrupt → ทุก thread ได้ BrokenBarrierException
6Advanceableได้ (countDown จาก thread ไหนก็ได้)ไม่ได้ (ต้อง await ทุก thread)
7Use Caseเริ่มงานพร้อมกัน, รอ initialization, parallel tasksParallel algorithms, phased computation, games

CountDownLatch - One-shot Countdown

หลักการ: Main thread เรียก await() รอ | Worker threads เรียก countDown() เมื่อเสร็จ

ตัวอย่าง: รอให้ Services เริ่มต้นเสร็จก่อนเริ่ม App

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CountDownLatchExample {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(3);
        CountDownLatch latch = new CountDownLatch(3); // รอ 3 tasks

        // Service 1: Database init
        executor.submit(() -> {
            try { Thread.sleep(2000); System.out.println("DB ready"); }
            catch (InterruptedException e) { Thread.currentThread().interrupt(); }
            finally { latch.countDown(); }
        });

        // Service 2: Cache warmup
        executor.submit(() -> {
            try { Thread.sleep(1500); System.out.println("Cache ready"); }
            catch (InterruptedException e) { Thread.currentThread().interrupt(); }
            finally { latch.countDown(); }
        });

        // Service 3: Config load
        executor.submit(() -> {
            try { Thread.sleep(1000); System.out.println("Config ready"); }
            catch (InterruptedException e) { Thread.currentThread().interrupt(); }
            finally { latch.countDown(); }
        });

        System.out.println("Waiting for services...");
        latch.await(); // Block จน count = 0
        System.out.println("All services started! Application ready.");
        
        executor.shutdown();
    }
}

Output

Waiting for services...
Config ready
Cache ready
DB ready
All services started! Application ready.

CyclicBarrier - Reusable Barrier

หลักการ: Threads เรียก await() รอกัน | เมื่อครบจำนวน → รัน barrier action (ถ้ามี) → ปล่อย threads ทั้งหมดพร้อมกัน → reset พร้อมรอบถัดไป

ตัวอย่าง: Parallel Processing แบบ Phases

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CyclicBarrierExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(4);
        
        // Barrier สำหรับ 4 threads + barrier action
        CyclicBarrier barrier = new CyclicBarrier(4, () -> 
            System.out.println("=== Phase completed! All threads synced ===")
        );

        for (int i = 1; i <= 4; i++) {
            final int id = i;
            executor.submit(() -> {
                try {
                    // Phase 1
                    System.out.println("Thread " + id + " - Phase 1 start");
                    Thread.sleep((long) (Math.random() * 1000));
                    System.out.println("Thread " + id + " - Phase 1 done, waiting...");
                    barrier.await(); // รอ thread อื่น
                    
                    // Phase 2
                    System.out.println("Thread " + id + " - Phase 2 start");
                    Thread.sleep((long) (Math.random() * 1000));
                    System.out.println("Thread " + id + " - Phase 2 done, waiting...");
                    barrier.await(); // รออีกรอบ (reusable!)
                    
                    // Phase 3
                    System.out.println("Thread " + id + " - Phase 3 done");
                    
                } catch (InterruptedException | BrokenBarrierException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }
        
        executor.shutdown();
    }
}

Output (ตัวอย่าง)

Thread 1 - Phase 1 start
Thread 2 - Phase 1 start
Thread 3 - Phase 1 start
Thread 4 - Phase 1 start
Thread 3 - Phase 1 done, waiting...
Thread 1 - Phase 1 done, waiting...
Thread 4 - Phase 1 done, waiting...
Thread 2 - Phase 1 done, waiting...
=== Phase completed! All threads synced ===
Thread 3 - Phase 2 start
Thread 1 - Phase 2 start
... (ทำซ้ำสำหรับ Phase 2, 3)

สรุป: เลือกอะไรดี?

สถานการณ์เหมาะสมเหตุผล
รอ initialization services เริ่มต้นแอปCountDownLatchOne-shot, main thread รอ workers
Parallel algorithm แบบหลาย phases (map-reduce)CyclicBarrierReusable, sync ทุก phase
รอ multiple threads ทำงานเสร็จก่อนรวมผลCountDownLatchSimple countdown
Game loop / Simulation stepsCyclicBarrierBarrier action ระหว่าง steps
ต้องการรัน callback เมื่อทุก thread มาถึงCyclicBarrierConstructor รับ Runnable

Related: Phaser (Java 7+)

Phaser ยืดหยุ่นกว่า CyclicBarrier - รองรับ dynamic registration/deregistration, multiple phases, tiered synchronization