掘金 后端 ( ) • 2024-04-15 06:51

忙等待(Busy-waiting)是一种同步机制,其中一个进程或线程重复检查某个条件是否满足以便继续执行,而不是进入休眠或阻塞状态。这个条件通常与某种资源或锁的可用性有关。忙等待常常与自旋锁相关联,因为自旋锁就是通过忙等待来检查锁的状态。

为什么要避免忙等待?

  1. CPU资源浪费:忙等待会占用处理器资源进行无意义的循环检查,而不释放处理器去执行其他可能的任务。
  2. 效率低下:特别是在单处理器系统中,忙等待的线程会阻塞其他线程的执行,使得整体系统效率降低。
  3. 响应时间延迟:忙等待会导致线程响应其他任务的延迟,因为它们被固定在检查条件上。

忙等待示例

下面是一个忙等待的简单示例,我们模拟一个场景,某个线程需要等待一个信号(变量signal)被另一个线程设置为true

public class BusyWaitExample {

    private static volatile boolean signal = false;

    public static void main(String[] args) throws InterruptedException {
        Thread waitingThread = new Thread(() -> {
            // 忙等待直到 signal 变为 true
            while (!signal) {
                // 忙等待循环体通常为空或者执行非阻塞操作
            }
            System.out.println("Signal received. Waiting thread is proceeding.");
        });

        Thread signallingThread = new Thread(() -> {
            try {
                Thread.sleep(2000); // 模拟计算或其他操作
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            signal = true; // 发送信号
            System.out.println("Signal sent.");
        });

        waitingThread.start();
        signallingThread.start();

        waitingThread.join();
        signallingThread.join();
    }
}

在这个例子中,waitingThread就是在进行忙等待。在signal变量被设置为true之前,它不停地检查signal的值而不做任何实际工作。

如何避免忙等待?

通常,我们可以通过使用某种阻塞机制(如等待/通知机制、信号量、互斥锁等)来避免忙等待。在Java中,可以使用wait()notify()notifyAll()方法,或者使用java.util.concurrent包中的工具类如LocksConditions

下面的代码展示了如何使用wait()notify()来避免忙等待:

public class WaitNotifyExample {

    private static final Object lock = new Object();
    private static boolean signal = false;

    public static void main(String[] args) throws InterruptedException {
        Thread waitingThread = new Thread(() -> {
            synchronized (lock) {
                while (!signal) {
                    try {
                        lock.wait(); // 阻塞而不是忙等待
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                }
                System.out.println("Signal received. Waiting thread is proceeding.");
            }
        });

        Thread signallingThread = new Thread(() -> {
            synchronized (lock) {
                try {
                    Thread.sleep(2000); // 模拟计算或其他操作
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                signal = true; // 发送信号
                lock.notify(); // 通知等待的线程
                System.out.println("Signal sent.");
            }
        });

        waitingThread.start();
        signallingThread.start();

        waitingThread.join();
        signallingThread.join();
    }
}

在修改后的例子中,waitingThread将不再忙等待,而是在signal没有被设置之前阻塞在wait()方法上。这样就避免了忙等待,线程在等待信号的时候不会消耗CPU资源。

总之,忙等待虽然在某些低延迟和高性能的场景下适用,但在大多数情况下,它是应该被避免的。通过使用适当的同步工具和方法可以有效提高程序效率,节省系统资源。