onSpinWait;()线程类的方法——Java9
在学习Java9特性时,我遇到了一个新的onSpinWait;()线程类的方法——Java9,java,multithreading,concurrency,java-threads,spinlock,Java,Multithreading,Concurrency,Java Threads,Spinlock,在学习Java9特性时,我遇到了一个新的Threadclass方法,名为。根据javadocs,此方法用于: 指示呼叫方暂时无法继续,直到 其他活动中一个或多个动作的发生 有人能给我一个真实的例子或场景来帮助我理解这个方法吗?纯系统提示 我引述如下: 目标 定义一个API,允许Java代码向运行时系统提示它处于旋转循环中。API将是一个纯粹的提示,并且不包含任何语义行为要求(例如,no-op是一个有效的实现)。允许JVM受益于特定于自旋循环的行为,这些行为在某些硬件平台上可能很有用。在JDK中提
Thread
class方法,名为。根据javadocs,此方法用于:
指示呼叫方暂时无法继续,直到
其他活动中一个或多个动作的发生
有人能给我一个真实的例子或场景来帮助我理解这个方法吗?纯系统提示
我引述如下:
目标
定义一个API,允许Java代码向运行时系统提示它处于旋转循环中。API将是一个纯粹的提示,并且不包含任何语义行为要求(例如,no-op是一个有效的实现)。允许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();
}
这将与没有方法调用时完全相同,但系统可以自由降低进程优先级;当这个循环的资源需要用于其他更重要的事情时,可以减慢循环或减少循环中的电量。它与x86操作码暂停
相同(可能编译为),与Win32宏YieldProcessor
、GCC的mm\u暂停()和C方法线程相同。SpinWait
这是一种非常弱的屈服形式:它告诉您的CPU您处于一个循环中,可能会消耗很多CPU周期等待发生某些事情(忙碌等待)
通过这种方式,CPU可以将更多资源分配给其他线程,而无需实际加载操作系统调度程序和退出准备运行的线程(这可能会很昂贵)
这种方法的一个常见用途是自旋锁,当您知道共享内存上的争用非常罕见或很快结束时,自旋锁的性能可能比普通锁更好
这类代码的伪代码可能如下所示:
int state = 0; //1 - locked, 0 - unlocked
routine lock:
while state.cas(new_value=1, wanted_value=0) == false //if state is 0 (unlocked), store 1 (locked) and return true, otherwise just return false.
yield
routine unlock:
atomic_store(state,0)
yield
可以通过Thread.onSpinWait()
实现,这意味着在尝试锁定锁时,CPU可以向其他线程提供更多资源
在实现无锁算法时,这种产生的技术非常常见和流行,因为它们大多数依赖于忙等待(这几乎总是作为原子比较和交换循环实现)。这有你能想象到的所有现实用途 在阅读了文档及其源代码之后,我只想添加我的2美分。此方法可能会触发一些优化,但可能不会-因此必须小心-您不能真正依赖它-因为这对CPU
来说是一个提示
,超出了需求,可能没有任何初始依赖方式。。。这意味着它的实际源代码如下所示:
@HotSpotIntrinsicCandidate
public static void onSpinWait() {}
这实际上意味着,根据一个真实示例的,该方法在命中JIT
中的c2编译器之前基本上是不可操作的,
假设您希望实现异步日志记录,其中希望记录某些内容的线程不希望等待其日志消息“发布”(比如写入文件),只要它最终会发布(因为它们有真正的工作要做)
比如说,您决定拥有一个专门的使用者线程,该线程专门负责将日志消息写入文件:
(Single)Consumer
while (concurrentQueue.isEmpty())
{
//what should I do?
}
writeToFile(concurrentQueue.popHead());
//loop
问题是如何在while块中执行?Java并没有提供理想的解决方案:您可以使用Thread.sleep(),但需要多长时间,这是很重要的;或者一个Thread.yield(),但这是未指定的,或者您可以使用锁或互斥锁*,但这通常太重,并且会降低生成程序的速度(并且会破坏异步日志记录的既定目的)
您真正想对运行时说的是,“我预计我不会等待太长时间,但我希望尽量减少等待的开销/对其他线程的负面影响”。
这就是Thread.onSpinWait()的作用
如上所述,在支持onSpinWait()的平台(如x86)上,onSpinWait()被内部化为暂停指令,这将为您带来所需的好处。因此:
(Single)Consumer
while (concurrentQueue.isEmpty())
{
Thread.onSpinWait();
}
writeToFile(concurrentQueue.popHead());
//loop
这可以改善“忙等待”式循环的延迟
我还想澄清,它不仅在实现“自旋锁”时有用(尽管在这种情况下它肯定有用);上面的代码不需要任何类型的锁(旋转或其他)
如果你想进入杂草丛中,最好的办法莫过于
*为了清楚起见,JVM在尝试最小化互斥锁的成本方面非常聪明,最初将使用轻量级锁,但这是另一个讨论。哦,该死的,是的!完全是我的错,读错了方向。这对我来说仍然很困惑。。。这将仅在C2编译器(也称为JIT)启动时使用。在那之前,什么都不会发生。
(Single)Consumer
while (concurrentQueue.isEmpty())
{
Thread.onSpinWait();
}
writeToFile(concurrentQueue.popHead());
//loop