Java中什么是快速、等待通知还是繁忙等待?

Java中什么是快速、等待通知还是繁忙等待?,java,multithreading,Java,Multithreading,我知道使用忙等待不是一种好的编程实践,最好尽可能使用同步对象(waitnotify)。但我想知道,如果一个人准备牺牲cpu周期,那么繁忙等待会更快还是等待通知更快 我假设wait notify将涉及对同步对象的内在锁定,并且信号可能来自内核以唤醒线程,这使得这种方法比繁忙的等待慢得多,在繁忙的等待中,只需不断检查一个条件,直到它得到满足。只要满足这个条件(例如布尔值==true),线程就可以从繁忙等待中走出来。根据我的理解,我觉得忙碌的等待应该更快 如果其他人能分享他们的想法并纠正我的论点错误,

我知道使用忙等待不是一种好的编程实践,最好尽可能使用同步对象(waitnotify)。但我想知道,如果一个人准备牺牲cpu周期,那么繁忙等待会更快还是等待通知更快

我假设wait notify将涉及对同步对象的内在锁定,并且信号可能来自内核以唤醒线程,这使得这种方法比繁忙的等待慢得多,在繁忙的等待中,只需不断检查一个条件,直到它得到满足。只要满足这个条件(例如布尔值==true),线程就可以从繁忙等待中走出来。根据我的理解,我觉得忙碌的等待应该更快


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

人们愿意牺牲繁忙等待的CPU周期,因为这会更快。 忙等待是实时低延迟应用程序的一个示例

有一个叫做伦敦证券交易所的框架,其中一个锁定策略是忙碌等待,这就是他们如何使用它

为了实现超快,最好在通知锁时浪费cpu周期,而不是缩短时间

你对所有其他东西都是对的,如果你在disruptor上搜索一下,阅读他们的文章,你会得到更多的澄清。关于高性能和低延迟,有太多的话要说


一个好的博客是

实验表明,与等待并通知(无论如何,在我的硬件上)相比,如果您忙着等待,您将更快地看到该标志。(详情如下。)差异非常小,因此这只适用于非常罕见的应用程序。股票交易应用程序,例如,公司在追逐他们能获得的任何优势(试图将服务器定位在交易所附近,以便在交易所的网络馈送中获得微秒的改进等)。我还可以想象一些科学应用

在绝大多数应用程序中,这种差异实际上根本没有区别

但CPU的情况当然是,其中一个核心硬钉:

这对影响机箱上的其他进程和数据中心的功耗都是不利的

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


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

WaitAndNotify.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));
    }
}
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周期,这反过来可能会使您的系统整体变慢

  • 所以现在你应该在什么时候使用忙等待。 正如Claudio所说,它主要用于低延迟系统。 但它仍然不能盲目使用。当您的制作人正在工作时,请使用“忙等待” 正在以稳定的速度生产。 如果您的生产商以可变速率生产项目(通常通过泊松分布证明),那么您可能应该使用wait notify

  • 通常,在高吞吐量和低延迟系统中,最好的折衷方法是 进行忙碌等待一段时间,然后转到等待()。 如果您的系统需要超低级别,那么您可以进行许多优化 其中一个可能正忙着等待。 但不应该是每个线程都在忙等待。确保只有一些消费者 可能大约有N/2消费者正在忙着等待,其中N是您计算机中的内核数 系统。浪费CPU周期可能会影响系统的整体性能和响应能力。 对于您的ref:即使是普通的ReentrantLock及其变体也应用这些策略。i、 e i、 e当线程调用lock.lock()时,它在进入队列之前尝试获取锁两次,并等待释放锁。对于低延迟系统,您甚至可以为特定场景定义自己的锁,在这些场景中,用户在进入队列之前会尝试10次以上(它们将是所谓的旋转锁的变体)


  • 视情况而定。有几种情况:

    场景A

    如果在“忙等待”中等待的是硬件操作(例如,从硬盘到内存读取扇区):

    1) 硬件将执行此操作

    2) 驱动程序将启动一个中断

    3) 操作将停止实际进程(您的忙等待进程),保存中断将在其进程中覆盖的任何CPU寄存器的实际值

    4) 中断将是处理,修改指示数据可用的任何标志(在磁盘读取的情况下)

    5) 将恢复所有重写的寄存器

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

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

    请记住,如果有另一个进程正在运行(操作系统进程、其他程序),它们会将您的进程置于进程尾部。此外,操作系统还可以决定,如果您的进程已经使用了它所能使用的所有CPU周期(它花费了它的数量),那么当它需要等待某个条件时,它的优先级将低于其他进入睡眠状态的进程(而不是使用忙等待方法)

    结语。如果没有其他外部进程在同一个co中运行(不太可能),则速度会更快
    while( !fileReady() ){
        ...
    }
    
    boolean exit = false;
    while( exit==false ){
        synchronize(var){
            if(var = CONDITIONS_MEET)
                exit=true;
        }
    }
    //operations...