在多线程环境中,“忙等待”是什么?
它如何有用,如何在Java多线程环境中实现?
它以何种方式有助于提高应用程序的性能?
在多线程环境中,“忙等待”是什么?
它如何有用,如何在Java多线程环境中实现?
它以何种方式有助于提高应用程序的性能?
其他回答中忽略了“忙等待”的真正问题。
除非你在处理需要节约电力的应用程序,否则烧掉CPU时间本身并不是什么坏事。只有当存在其他准备好运行的线程或进程时,它才是“坏事”。而当一个处于准备就绪状态的线程卡在正在进行忙等待的循环中时,情况变得更加严重。
这才是真正的问题所在。在普通操作系统上运行普通用户模式程序时,无法控制哪些线程在哪个处理器上运行;操作系统也无法区分正在忙等待和正在工作的线程;即使操作系统知道线程在进行忙等待,也无法知道线程在等待什么。
因此,在一个忙等待的循环中,等待事件的线程可能会等待多毫秒(几乎是永远),而唯一能够发生该事件的线程却在一旁等待使用CPU。
忙等待通常用于系统中需要精细控制线程在哪个处理器上运行的情况下。当你知道将引起事件的线程实际上在另一个处理器上运行时,忙等待可能是等待事件最高效的方式。这通常发生在编写操作系统本身的代码或编写在实时操作系统下运行的嵌入式实时应用程序时。
Kevin Walters提到了一个等待时间非常短的情况。在一个普通操作系统上运行的CPU密集型普通程序每个时间片可能会被允许执行数百万条指令。因此,如果程序使用自旋锁来保护由几条指令组成的临界区域,那么当线程处于临界区域时,它很有可能不会失去时间片。也就是说,如果线程A发现自旋锁已经被锁定,那么持有锁的线程B很有可能实际上正在不同的CPU上运行。这就是为什么在知道程序将在多处理器主机上运行时,在普通程序中使用自旋锁是可以接受的原因。
Busy spin是一种不释放CPU等待事件的技术之一。它通常用于避免在线程暂停并在其他核心中恢复时丢失CPU缓存中的数据。
因此,如果您正在处理低延迟系统,其中您的订单处理线程当前没有任何订单,而不是睡眠或调用wait()
,您可以循环并再次检查新消息队列。仅当您需要等待非常短的时间(例如微秒或纳秒)时才有益。
LMAX Disrupter framework是一个高性能的线程间通信库,具有基于这个概念和使用忙旋转循环的BusySpinWaitStrategy,用于在障碍上等待EventProcessors。
忙等待或自旋是一种技术,在这种技术中,进程会重复检查条件是否为真,而不是调用wait或sleep方法并释放CPU。
1.它主要在多核处理器中有用,其中条件很快就会变为真,即在毫秒或微秒内。
2.不释放CPU的优点是,所有缓存的数据和指令都保持不受影响,如果该线程被挂起在一个核上并带回到另一个线程,则可能会丢失这些数据和指令。
从性能角度来看,繁忙的自旋/等待通常是不好的。在大多数情况下,最好睡眠并等待信号,而不是进行自旋。考虑这样一种情况,有两个线程,线程1正在等待线程2设置一个变量(比如说,它会等待直到 var == true
)。然后,它将通过简单地执行
while (var == false)
;
while (var == false) Thread.yield();
是否适用?或者在自旋时没有(好的)情况可以使用Thread.yield()
吗? - vanOekel忙碌自旋就是循环等待线程完成。例如,您有10个线程,并且希望等待所有线程完成后再继续执行。
while(ALL_THREADS_ARE_NOT_COMPLETE);
//Continue with rest of the logic
ExecutorService
来管理多个线程。 ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
Runnable worker = new WorkerThread('' + i);
executor.execute(worker);
}
executor.shutdown();
//With this loop, you are looping over till threads doesn't finish.
while (!executor.isTerminated());
它太忙碌了,因为它消耗了资源,CPU并不处于闲置状态,而是一直在循环运行。我们应该有机制来通知主线程(父线程)表示所有线程都已完成,可以继续执行其余任务。
通过前面的示例,我们可以使用不同的机制来改善性能,而不是进行繁忙的自旋处理。
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
Runnable worker = new WorkerThread('' + i);
executor.execute(worker);
}
executor.shutdown();
try {
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
log.fatal("Exception ",e);
}