在Java中什么是快速,等待通知或忙等待?

我知道使用busy-wait不是一个好的编程习惯,最好尽可能使用同步对象(wait-notify)。 但是我想知道一个人是否准备好牺牲cpu周期,然后忙着等待更快或等待通知?

我假设wait-notify将涉及对同步对象的内部锁定,并且信号可能来自内核以唤醒线程,使得这种方法比忙碌等待慢得多,其中人们可以直接检查条件直到它是满意。 只要满足此条件(例如,布尔值== true),线程就可以从繁忙的等待中退出。 根据我的理解,我感到很忙 – 等待应该更快。

如果我的论点是错误的,如果其他人能分享他们的想法并纠正我,我将不胜感激。

实验表明,如果你等待并等待并通知(在我的硬件上,无论如何),你会更快地看到旗帜。 (详情如下。)差异非常非常小 ,所以这只适用于非常罕见的应用程序。 例如,股票交易应用程序,公司可以获得他们可以获得的任何优势(争取尽可能在交易所附近找到他们的服务器,以便从交易所获得微网改进等)可能会认为差异是值得的。 我也可以想象一些科学应用。

在绝大多数应用程序中,差异实际上没有任何区别。

但是,CPU发生的事情当然是其中一个核心硬件:

hardpeg

在影响盒子上的其他进程和数据中心的功耗方面,这是不好的。

所以:极度不情愿地使用,只有在真正重要的情况下才能使用。


数据(非常小的样本,但代码如下):

忙碌的等待:10631 12350 15278
等待并通知:87299 120964 107204
 Delta:76668 108614 91926

时间是纳秒 。 十亿分之一秒。 上面的平均增量为92403ns(0.092402667毫秒,0.000092403秒)。

BusyWait.java

 public class BusyWait { private static class Shared { public long setAt; public long seenAt; public volatile boolean flag = false; } public static void main(String[] args) { final Shared shared = new Shared(); Thread notifier = new Thread(new Runnable() { public void run() { System.out.println("Running"); try { Thread.sleep(500); System.out.println("Setting flag"); shared.setAt = System.nanoTime(); shared.flag = true; } catch (Exception e) { } } }); notifier.start(); while (!shared.flag) { } shared.seenAt = System.nanoTime(); System.out.println("Delay between set and seen: " + (shared.seenAt - shared.setAt)); } } 

WaitAndNotify.java

 public class WaitAndNotify { private static class Shared { public long setAt; public long seenAt; public boolean flag = false; } public static void main(String[] args) { (new WaitAndNotify()).test(); } private void test() { final Shared shared = new Shared(); final WaitAndNotify instance = this; Thread notifier = new Thread(new Runnable() { public void run() { System.out.println("Running"); try { Thread.sleep(500); System.out.println("Setting flag"); shared.setAt = System.nanoTime(); shared.flag = true; synchronized (instance) { instance.notify(); } } catch (Exception e) { } } }); notifier.start(); while (!shared.flag) { try { synchronized (this) { wait(); } } catch (InterruptedException ie) { } } shared.seenAt = System.nanoTime(); System.out.println("Delay between set and seen: " + (shared.seenAt - shared.setAt)); } } 

一个人准备在繁忙的等待中牺牲CPU周期,因为它更快。 忙碌等待是实时低延迟应用的示例。

有一个名为lmax disruptor的框架是为伦敦证券交易所建立的,其中一个锁定策略是忙等待,这就是他们如何使用它。

为了达到超快速度,在通知锁定时,最好浪费cpu周期。

你对所有其他的东西都是正确的,如果你在破坏者上稍微阅读并阅读他们的论文,你会得到更多的澄清。 关于高性能和低延迟的事情太多了。

一个好看的博客是机械同情 。

忙碌等待比正常等待通知更快。

  1. 但是你为什么要等? 因为一个生产者或其他线程会做一些工作,然后设置一个条件(或通知),以便你实际上可以退出忙/等待循环。 现在假设你的制作人正在做一些繁重的任务,那么你实际上是通过忙碌等待(主要是在单处理器系统中)来吃掉它的cpu周期,这反过来可能会使你的系统整体变慢。

  2. 所以现在你应该使用忙等待。 正如Claudio所说,它主要用于低延迟系统。 但仍然不能盲目使用它。 当生产者以稳定的速度生产时,请使用忙等待。 如果您的生产者以可变的速率生产物品(通常由泊松分布certificate),那么您应该使用等待通知。

  3. 通常,高吞吐量和低延迟系统的最佳权衡是等待一段时间忙,然后转到wait()。 如果您的系统需要Ultra LowLtency,那么您可以进行许多优化,其中一个可能是Busy-Wait。 但它不应该是每个线程都忙着等待。 确保只有一些消费者可能在N / 2左右。消费者正忙着等待N是系统中的核心数量。 浪费CPU周期可能会影响系统的整体性能和响应能力。 对于您的参考:Even Normal ReentrantLock及其变体适用于这些策略。 我,即当一个线程调用lock.lock()时,它尝试在进入队列之前获取锁两次并等待锁被释放 。 对于低延迟系统,您甚至可以为进入队列之前尝试10次以上的特定场景定义自己的锁(它们将是所谓的自旋锁的变体)

要看。 有几种情况:

情景A

如果在’busy-wait’中等待的是硬件操作(例如,从硬盘读取扇区到内存):

1)硬件将执行操作。

2)驱动程序将启动中断。

3)操作将暂停实际过程(您的忙等待过程),保存任何CPU注册中断将在其处理中覆盖的实际值。

4)中断将处理,修改指示数据可用的任何标志(如果是磁盘读取)。

5)将恢复任何覆盖寄存器。

6)您的流程将继续其流程。 只是在下一次迭代中,它将调用循环的conthe条件。 例如,如果忙等待是:

 while( !fileReady() ){ ... } 

fileReady()方法将是一个内部将检查是否设置了具体标志(在4中修改的标志)的方法。 7)因此,只需在下一次迭代中,循环将进入并执行操作。

请记住,如果有另一个进程在运行(操作系统进程,其他程序),它们会将您的进程置于进程尾。 此外,操作系统可以决定,鉴于您的进程已经使用了他可以使用的所有CPU周期(它花费了它的量子),它将具有较少的优先级,其他进程进入hibernate状态(而不是使用忙等待方法)需要等待一定的条件。

结论。 如果在同一个内核/ CPU中没有其他外部进程正在运行(非常不可能),则会更快。


情景B

另一方面,如果busy方法正在等待另一个进程结束(或将任何变量设置为某个值,则busy-wait将更慢。

1)busy方法将在CPU中运行。 由于其他进程没有运行,因此条件不能更改,因此busy方法将一直运行,直到CPU决定为另一个进程提供CPU时间。

2)其他进程将运行。 如果这个过程花费时间而没有达到繁忙等待过程所需的配置,那么转到1),否则继续3)

3)其他(不是忙等待)进程仍将运行一段时间,直到cpu决定新的进程更改。

4)busy-method将再次运行,但现在条件已满足,因此操作现在已完成。

结论:它更慢,我们也减慢了整个过程。


情景C.

如果我们有与B相同的场景,但有多个核心(每个核心中有一个进程)怎么办?

首先,请记住,即使您有一个具有多个内核的CPU,您的程序也可能不允许使用多个内核。 也许有一些操作系统或其他程序使用它们。

其次,它不值这个价。 请记住,该过程必须通信以允许忙碌等待发现条件满足。 这通常可以通过’final’变量来完成,因此每次评估条件时都需要在同步块中输入(在进入循环之前无法锁定并且没有解锁它,因为在其他过程中是这样)将无法更改变量。所以你需要这样的东西:

 boolean exit = false; while( exit==false ){ synchronize(var){ if(var = CONDITIONS_MEET) exit=true; } } //operations... 

}

但是,wait-notify会做类似的,更高效的(在语言级别),而不会浪费CPU周期,并使用良好的prectices !!

结论:你的生活很复杂,不太可能会更快(非常非常不可能)。


最后的结论:只有当您了解操作系统的具体细节以及运行程序的环境时,才会处于非常简单的场景中,您可以考虑使用忙等待方法。

我希望这会解决你的问题。 不要犹豫,问一下是不是很清楚。