忙等待(Busy-waiting)是一种同步机制,其中一个进程或线程重复检查某个条件是否满足以便继续执行,而不是进入休眠或阻塞状态。这个条件通常与某种资源或锁的可用性有关。忙等待常常与自旋锁相关联,因为自旋锁就是通过忙等待来检查锁的状态。
为什么要避免忙等待?
- CPU资源浪费:忙等待会占用处理器资源进行无意义的循环检查,而不释放处理器去执行其他可能的任务。
- 效率低下:特别是在单处理器系统中,忙等待的线程会阻塞其他线程的执行,使得整体系统效率降低。
- 响应时间延迟:忙等待会导致线程响应其他任务的延迟,因为它们被固定在检查条件上。
忙等待示例
下面是一个忙等待的简单示例,我们模拟一个场景,某个线程需要等待一个信号(变量signal
)被另一个线程设置为true
。
java
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
包中的工具类如Locks
和Conditions
。
下面的代码展示了如何使用wait()
和notify()
来避免忙等待:
java
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资源。
总之,忙等待虽然在某些低延迟和高性能的场景下适用,但在大多数情况下,它是应该被避免的。通过使用适当的同步工具和方法可以有效提高程序效率,节省系统资源。