Java 如何让主线程阻塞

在Java编程中,有时我们需要让主线程阻塞,以等待某些操作完成,例如等待子线程执行完毕、等待某个条件的满足、或者等待某个资源的可用性等。下面将详细介绍几种常见的阻塞主线程的方法,每种方法都附有示例代码,便于理解。

1. 使用 Thread.sleep()

Thread.sleep(long millis) 方法可以让当前线程休眠指定时间。在此期间,该线程将处于阻塞状态。

示例代码

public class SleepExample {
    public static void main(String[] args) {
        System.out.println("主线程开始执行");
        try {
            // 让主线程休眠2000毫秒
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("主线程结束执行");
    }
}

代码说明:

在这个例子中,主线程开始执行后,会调用 Thread.sleep(2000),然后在主线程中停顿2000毫秒,模拟一个阻塞的状态。

2. 使用 Object.wait()

在Java中,Object 类提供了 wait() 方法,这个方法可以让当前线程等待,直到另一个线程调用 notify()notifyAll() 方法。

示例代码

class WaitNotifyExample {
    private static final Object lock = new Object();

    public static void main(String[] args) {
        Thread workerThread = new Thread(() -> {
            synchronized (lock) {
                try {
                    System.out.println("子线程开始工作,准备阻塞主线程");
                    lock.wait(); // 阻塞主线程
                    System.out.println("子线程结束工作");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        workerThread.start();

        try {
            // 让主线程睡眠一段时间
            Thread.sleep(2000);
            synchronized (lock) {
                System.out.println("主线程准备释放子线程");
                lock.notify(); // 释放子线程
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

代码说明:

此示例中,创建了一个子线程并让其在 lock 对象上调用 wait(),此时主线程并不会立即退出,而是会等待 lock.notify() 的调用。

3. 使用 CountDownLatch

CountDownLatch 是一种用来控制一个或多个线程等待一组操作完成的同步辅助类。

示例代码

import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {
    public static void main(String[] args) {
        CountDownLatch latch = new CountDownLatch(1); // 初始化计数为1

        new Thread(() -> {
            try {
                System.out.println("子线程正在工作...");
                Thread.sleep(2000); // 模拟工作
                latch.countDown(); // 减少计数
                System.out.println("子线程工作完成,释放主线程");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        try {
            System.out.println("主线程等待子线程完成...");
            latch.await(); // 阻塞主线程
            System.out.println("主线程继续执行");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

代码说明:

在这个例子中,创建了一个 CountDownLatch,主线程调用 latch.await() 方法,这会使主线程阻塞,直到子线程调用 latch.countDown() 释放这个计数器。

4. 使用 Future 接口

在并发编程中,Future 接口可以帮助我们获取异步计算的结果。同时它也可以用来让主线程等待其他线程的结果。

示例代码

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class FutureExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newCachedThreadPool();
        
        Future<String> future = executor.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                Thread.sleep(2000); // 模拟长时间任务
                return "任务完成";
            }
        });

        try {
            System.out.println("主线程等待任务完成...");
            String result = future.get(); // 阻塞主线程,直到任务完成
            System.out.println("结果: " + result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        } finally {
            executor.shutdown();
        }
    }
}

代码说明:

在此示例中,通过 Future.get() 方法,主线程会阻塞,直到子线程计算出结果。

结论

通过以上几种方法,我们可以有效地让主线程阻塞,根据不同的需要选择最合适的方案。在实际项目中,合理使用这些技术可以确保程序的高效与稳定。在高并发场景中,了解和掌握这些阻塞方法至关重要。此外,开发者需要根据应用程序的具体需求,选择合适的同步工具,以提高性能和可维护性。

关系图

我们可以用Mermaid的ER图来展示这些关系:

erDiagram
    主线程 {
        string 状态
    }
    子线程 {
        string 工作状态
    }
    CountDownLatch {
        int 值
    }

序列图

下面的序列图展示了主线程、子线程与 CountDownLatch 之间的协调过程:

sequenceDiagram
    participant 主线程
    participant 子线程
    participant CountDownLatch
    主线程->>子线程: 启动子线程
    子线程->>CountDownLatch: 等待状态
    主线程->>CountDownLatch: await()
    子线程->>子线程: 执行任务
    子线程->>CountDownLatch: countDown()
    CountDownLatch->>主线程: 释放

通过以上的方法和示例,我们可以充分理解如何在Java中阻塞主线程,希望这些内容能够对你的学习和工作有所帮助。