在学习Java 9时,我遇到了Thread
类的新方法,称为onSpinWait
。根据javadocs,这个方法用于此:
表示调用者暂时无法进展,直到 其他活动发生一项或多项行动。
有人可以帮助我理解这种方法给出一个真实的例子吗?
答案 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 compiler
,this
答案 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在尝试最小化互斥锁的成本方面非常聪明,并且最初将使用轻量级锁,但这是另一个讨论。