Thread类的onSpinWait()方法

时间:2017-06-19 04:43:02

标签: java multithreading

在学习Java 9时,我遇到了Thread类的新方法,称为onSpinWait​。根据javadocs,这个方法用于此:

  

表示调用者暂时无法进展,直到   其他活动发生一项或多项行动。

有人可以帮助我理解这种方法给出一个真实的例子吗?

4 个答案:

答案 0 :(得分:19)

与x86操作码PAUSE相同(并且可能编译)并且等效于Win32宏YieldProcessor,GCC' s __mm_pause()和C#方法{ {1}}

这是一种非常弱化的屈服形式:它告诉你的CPU你处于循环中,可能会燃烧一些相当频繁的CPU循环(忙等待)。

这样,CPU可以为其他线程分配更多资源,而无需实际加载OS调度程序并使睡眠线程出列(这可能很昂贵)。

一个常见的用途就是自旋锁定,当你知道共享内存上的争用非常罕见或很快完成时,自旋锁可能比普通锁更好。

这样的伪代码可能如下所示:

Thread.SpinWait

int state = 0; //1 - locked, 0 - unlocked routine lock: while cas(state, 1) == 1 yield routine unlock: atomic_store(state,0) 可以用yield来实现,暗示在尝试锁定锁时,CPU可以为其他线程提供更多资源。

PS。这种屈服技术在实现无锁算法时非常普遍和流行,因为它们中的大多数依赖于忙等待(几乎总是作为原子CAS循环实现)。这有你可以想象的每一个真实世界的用途。

答案 1 :(得分:16)

纯系统暗示!

阅读this article我引用:

  

<强>目标

     

定义一个API,允许Java代码提示它处于旋转循环中的运行时系统。 API将是纯粹的提示,并且不会带有语义行为要求(例如,无操作是有效的实现)。允许JVM受益于可能在某些硬件平台上有用的自旋循环特定行为。在JDK中提供无操作实现和内部实现,并在至少一个主要硬件平台上演示执行优势。

有很多次线程必须暂停,直到其范围之外的某些内容发生变化。 (曾经)常见的做法是wait() notify()模式,其中有一个线程等待另一个线程唤醒它们。

这有一个很大的限制,即,另一个线程必须知道可能有等待线程并且应该通知。如果另一个主题的工作超出了您的控制范围,则无法获得通知。

唯一的方法是旋转等待。让我们假设您有一个程序来检查新电子邮件并通知用户:

while(true) {
    while(!newEmailArrived()) {
    }
    makeNotification();
}

这段代码每秒会执行数百万次;使用宝贵的电力和CPU电力一遍又一遍地旋转。执行此操作的常见方法是在每次迭代时等待几秒钟。

while(true) {
    while(!newEmailArrived()) {
        try {
            Thread.sleep(5000);
        } catch(InterruptedException e) {
        }
    }
    makeNotification();
}

这样做非常好。但是,如果你必须立即工作,睡眠可能是不可能的。

Java 9尝试通过引入这种新方法来解决这个问题:

while(true) {
    while(!newEmailArrived()) {
        Thread.onSpinWait();
    }
    makeNotification();
}

这与没有方法调用完全相同,但系统可以自由降低进程优先级;当其他更重要的事情需要资源时,减慢循环或减少电路上的电力。

答案 2 :(得分:7)

我只想在阅读文档及其源代码后添加2美分。这个方法可能会触发一些优化而可能不会 - 所以必须小心 - 你不能真正依赖它 - 因为public class Query { private int size; private int offset; private String queryString; private List<Field> select; private List<Filter> filters; private String filterString; private SortOn sortOn; private List<Aggregation> aggregations; private Histogram histogram; private GraphQuery graphQuery; private boolean highlight; //setters and getters } hint更多不管是需求,可能无论如何都没有最初的依赖......意味着它的实际源代码如下所示:

CPU

这实际上意味着这个方法基本上是一个NO-OP,直到它@HotSpotIntrinsicCandidate public static void onSpinWait() {} c2 compilerthis

答案 3 :(得分:6)

对于一个真实世界的例子, 假设你想要实现异步日志记录,那里想要记录某些东西的线程,不想等待他们的日志消息被发布&#34; (写到文件中),只要它最终确实如此(因为他们已经有了真正的工作要做。)

Producer(s):
concurrentQueue.push("Log my message")

并且说,您决定拥有一个专用的消费者线程,它完全负责将日志消息实际写入文件:

(Single)Consumer

while (concurrentQueue.isEmpty())
{
    //what should I do?

}
writeToFile(concurrentQueue.popHead());
//loop

问题是在while块内部做什么? Java没有提供理想的解决方案:你可以做一个Thread.sleep(),但是有多长时间和那个重量级的人;或者一个Thread.yield(),但是没有指定,或者你可以使用锁或互斥*,但这通常太重量级并且也会减慢生产者的速度(并且会破坏异步的既定目的)记录)。

你真正想要的是对运行时说,&#34;我预计我不会等待太长时间,但我想减少等待/负面影响的其他开销线程&#34 ;. 这就是Thread.onSpinWait()的用武之地。

作为上述响应,在支持它的平台上(如x86),onSpinWait()被内省化为PAUSE指令,这将为您提供所需的好处。所以:

(Single)Consumer

while (concurrentQueue.isEmpty())
{
    Thread.onSpinWait();

}
writeToFile(concurrentQueue.popHead());
//loop

已经shown empirically这可以改善“忙碌等待”的等待时间。风格循环。

我还想澄清一点,它不仅仅有助于实现&#34;旋转锁定&#34; (虽然它在这种情况下绝对有用);上面的代码不需要任何类型的锁定(旋转或其他)。

如果你想进入杂草,你不能做得比Intel's specs

更好

*为了清楚起见,JVM在尝试最小化互斥锁的成本方面非常聪明,并且最初将使用轻量级锁,但这是另一个讨论。