Java的Semaphore:等待所有子线程执行结束再执行主线程

在Java编程中,并发编程是一个常见且重要的主题。尤其是在需要多个线程同时执行时,如何有效地管理这些线程的状态十分关键。Semaphore是Java并发包中的一个重要工具,可以用来控制对共享资源的访问。在本篇文章中,我们将深入探讨Semaphore的使用,并通过一个示例来展示如何在Java中使用Semaphore等待所有子线程执行结束,再执行主线程。

Semaphore简介

Semaphore是一个计数信号量,用于控制同时访问某个特定资源的线程数量。Semaphore的基本原理是维护一个计数器,当计数器大于0时,可以允许相应数量的线程进入临界区;当计数器为0时,线程就会被阻塞。

在本文的示例中,我们将创建若干个子线程,使用Semaphore来确保子线程完成后,主线程再进行后续操作。

类图

下面是我们示例中的类图:

classDiagram
    class MainThread {
        +main(String[] args)
    }
    class WorkerThread {
        +run()
    }
    MainThread --> WorkerThread : spawns

示例代码

接下来,我们通过一个具体的代码示例来演示如何使用Semaphore等待所有子线程执行结束。在这个示例中,我们将创建一个主线程和多个子线程,使用Semaphore来同步它们的执行。

import java.util.concurrent.Semaphore;

public class MainThread {
    private static final int THREAD_COUNT = 5; // 子线程数量
    private static final Semaphore semaphore = new Semaphore(0); // 初始化信号量为0
    
    public static void main(String[] args) {
        for (int i = 0; i < THREAD_COUNT; i++) {
            new WorkerThread().start(); // 启动子线程
        }

        try {
            // 等待所有子线程执行完毕
            for (int i = 0; i < THREAD_COUNT; i++) {
                semaphore.acquire(); // 获取信号量,这里会阻塞主线程
            }
            System.out.println("所有子线程执行完毕,主线程继续...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    static class WorkerThread extends Thread {
        @Override
        public void run() {
            try {
                // 模拟子线程工作
                System.out.println(Thread.currentThread().getName() + " 正在执行...");
                Thread.sleep((long) (Math.random() * 1000)); // 随机休眠

                System.out.println(Thread.currentThread().getName() + " 执行完毕.");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                semaphore.release(); // 执行完毕后释放信号量
            }
        }
    }
}

代码说明

在上述代码中,可以观察到以下关键点:

  1. 信号量的初始化:我们定义一个信号量 semaphore,并将其初始值设为0。这样,在主线程中调用 acquire() 时会被阻塞,直到有线程调用 release()

  2. 子线程的执行WorkerThread 类继承自 Thread,在 run() 方法中模拟了子线程的执行过程,最后调用 semaphore.release() 来通知主线程当前子线程已经执行完毕。

  3. 主线程的等待:主线程通过循环调用 semaphore.acquire() 方法,直到所有子线程的执行结束。每调用一次 acquire(),计数器减1;每次子线程执行完毕并调用 release(),计数器加1。

结论

Semaphore在并发编程中是一个非常有用的工具,它可以帮助我们进行有效的线程管理。在本文中,我们展示了如何利用Semaphore来确保主线程在所有子线程执行结束后再继续执行。这样的设计可以避免主线程提前执行导致的潜在问题。

在实际的应用中,熟练掌握Semaphore的使用技巧,可以使得代码更加健壮和高效。随着对Java并发编程的深入理解,我们可以在更复杂的场景中灵活应用这些概念,从而提升自己的编程能力。希望本文能够帮助你更好地理解Java中的Semaphore及其应用。