Java面试要点106 - Java CountDownLatch源码解析

发布于:2025-02-11 ⋅ 阅读:(38) ⋅ 点赞:(0)

在这里插入图片描述

引言

CountDownLatch是Java并发编程中一个重要的同步工具,它能够使一个或多个线程等待其他线程完成各自的工作。本文将深入分析CountDownLatch的源码实现,探讨其内部原理和实际应用场景,帮助开发者更好地理解和使用这个工具类。

一、CountDownLatch基本原理

CountDownLatch的核心思想是通过一个计数器来实现线程间的协调。当创建CountDownLatch实例时,需要指定一个初始计数值。线程可以调用await()方法等待计数器降为零,而其他线程可以通过调用countDown()方法减少计数。这种机制使得CountDownLatch成为处理线程间等待与通知的有效工具。

public class CountDownLatchExample {
    public static void main(String[] args) throws InterruptedException {
        // 创建CountDownLatch实例,初始计数为3
        CountDownLatch latch = new CountDownLatch(3);
        
        // 启动三个工作线程
        for (int i = 0; i < 3; i++) {
            int finalI = i;
            new Thread(() -> {
                try {
                    Thread.sleep(1000); // 模拟任务执行
                    System.out.println("Worker " + finalI + " 完成任务");
                    latch.countDown(); // 减少计数
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }).start();
        }
        
        // 主线程等待所有工作线程完成
        latch.await();
        System.out.println("所有工作线程已完成任务");
    }
}

二、源码结构分析

CountDownLatch的实现依赖于AbstractQueuedSynchronizer(AQS)。通过继承AQS,CountDownLatch能够利用AQS提供的强大同步机制。在源码中,CountDownLatch通过内部类Sync扩展AQS,实现了所需的同步语义。

public class CountDownLatchAnalysis {
    /**
     * CountDownLatch的核心源码结构
     */
    private static class Sync extends AbstractQueuedSynchronizer {
        // 构造函数设置初始状态(计数值)
        Sync(int count) {
            setState(count);
        }
        
        // 获取当前计数
        int getCount() {
            return getState();
        }
        
        // 实现tryAcquireShared方法
        protected int tryAcquireShared(int acquires) {
            return (getState() == 0) ? 1 : -1;
        }
        
        // 实现tryReleaseShared方法
        protected boolean tryReleaseShared(int releases) {
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c - 1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }
    }
}

三、await方法实现分析

await方法是CountDownLatch的关键方法之一,它使调用线程进入等待状态,直到计数器降为零。源码中的await方法通过AQS的共享模式实现线程同步。当计数器不为零时,调用线程会被加入AQS的等待队列。

public class AwaitMethodAnalysis {
    public static void explainAwaitImplementation() {
        /**
         * await方法的核心实现逻辑:
         * 1. 尝试获取共享锁
         * 2. 如果获取失败,将线程加入等待队列
         * 3. 等待被其他线程唤醒
         */
        CountDownLatch latch = new CountDownLatch(2);
        try {
            // await方法实际调用AQS的acquireSharedInterruptibly
            latch.await();  // 相当于sync.acquireSharedInterruptibly(1);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
    
    // await方法支持超时机制
    public static void awaitWithTimeout() {
        CountDownLatch latch = new CountDownLatch(2);
        try {
            // 设置等待超时时间
            boolean completed = latch.await(5, TimeUnit.SECONDS);
            if (!completed) {
                System.out.println("等待超时,任务未完成");
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

四、countDown方法实现分析

countDown方法用于减少计数器的值,当计数器降为零时,所有等待的线程都会被唤醒。这个方法的实现使用了AQS的释放共享锁机制,确保线程安全的计数器递减和线程唤醒操作。

public class CountDownMethodAnalysis {
    public static void explainCountDownImplementation() {
        /**
         * countDown方法的实现原理:
         * 1. 通过CAS操作减少计数器值
         * 2. 如果计数器降为零,唤醒所有等待线程
         */
        CountDownLatch latch = new CountDownLatch(1);
        
        // 示例:多线程调用countDown
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                try {
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName() + " 调用countDown");
                    latch.countDown();  // 实际调用sync.releaseShared(1)
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }, "Thread-" + i).start();
        }
    }
}

五、实际应用场景分析

CountDownLatch在实际开发中有着广泛的应用场景。它常用于实现并行任务的协调、等待服务启动完成、测试框架等场景。以下是一个模拟多服务启动协调的示例。

public class CountDownLatchUsage {
    public static class ServiceStarter {
        private final CountDownLatch latch;
        private final List<String> services;
        
        public ServiceStarter(List<String> services) {
            this.services = services;
            this.latch = new CountDownLatch(services.size());
        }
        
        public void startServices() {
            // 并行启动所有服务
            for (String service : services) {
                new Thread(() -> {
                    try {
                        System.out.println("正在启动服务: " + service);
                        // 模拟服务启动过程
                        Thread.sleep((long) (Math.random() * 2000));
                        System.out.println("服务已启动: " + service);
                        latch.countDown();
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                }).start();
            }
        }
        
        public void waitForServicesStart() throws InterruptedException {
            latch.await();
            System.out.println("所有服务已启动完成");
        }
    }
}

总结

通过深入分析CountDownLatch的源码实现,我们可以看到它是如何基于AQS框架实现线程同步的。CountDownLatch的设计简洁而强大,能够有效地解决多线程协调问题。在实际开发中,理解CountDownLatch的内部实现原理对于正确使用这个工具类至关重要。也要注意CountDownLatch是一次性的,计数器归零后就不能再重用,这一特性需要在设计系统时充分考虑。对于需要重复使用的场景,可以考虑使用CyclicBarrier等其他同步工具。通过合理使用CountDownLatch,可以更好地实现复杂的并发控制逻辑,提升系统的可靠性和性能。


网站公告

今日签到

点亮在社区的每一天
去签到