Jok*_*ker 29 java concurrency multithreading spinlock java-threads
在学习Java 9时,我遇到了一种新的Thread类方法,叫做onSpinWait?.根据javadocs,这个方法用于此:
表示调用者暂时无法进展,直到其他活动发生一个或多个操作为止.
有人能帮助我理解这种方法给出一个真实的例子吗?
Dav*_*aim 20
它与x86操作码相同(并且可能编译)PAUSE并且等效于Win32宏YieldProcessor,GCC __mm_pause()和C#方法Thread.SpinWait
这是一种非常弱化的屈服形式:它告诉你的CPU你处于一个循环中,可能会燃烧一些相当于CPU发生的循环等待发生的事情(忙碌等待).
这样,CPU可以将更多资源分配给其他线程,而无需实际加载OS调度程序并使休眠线程出列(这可能很昂贵).
一个常见的用途是自旋锁定,当你知道共享内存上的争用非常罕见或很快完成时,自旋锁可能比普通锁更好.
这样的伪代码可能如下所示:
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)
Run Code Online (Sandbox Code Playgroud)
yield可以实现Thread.onSpinWait(),暗示在尝试锁定锁时,CPU可以为其他线程提供更多资源.
PS.这种屈服技术在实现无锁算法时非常普遍和流行,因为它们中的大多数依赖于忙等待(几乎总是作为原子CAS循环实现).这有你可以想象的每一个真实世界的用途.
Mor*_*hai 17
纯系统暗示!
阅读这篇文章我引用:
目标
定义一个API,允许Java代码提示它处于旋转循环中的运行时系统.API将是纯粹的提示,并且不会带有语义行为要求(例如,无操作是有效的实现).允许JVM受益于可能在某些硬件平台上有用的自旋循环特定行为.在JDK中提供无操作实现和内部实现,并在至少一个主要硬件平台上演示执行优势.
有很多次线程必须暂停,直到其范围之外的某些内容发生变化.(曾经)常见的做法是wait() notify()有一个线程等待另一个线程唤醒它们的模式.
这有一个很大的限制,即,另一个线程必须知道可能存在等待线程并且应该通知.如果其他线程的工作超出您的控制范围,则无法获得通知.
唯一的方法是旋转等待.假设您有一个检查新电子邮件的程序并通知用户:
while(true) {
while(!newEmailArrived()) {
}
makeNotification();
}
Run Code Online (Sandbox Code Playgroud)
这段代码每秒会执行数百万次; 使用宝贵的电力和CPU电力一遍又一遍地旋转.执行此操作的常见方法是在每次迭代时等待几秒钟.
while(true) {
while(!newEmailArrived()) {
try {
Thread.sleep(5000);
} catch(InterruptedException e) {
}
}
makeNotification();
}
Run Code Online (Sandbox Code Playgroud)
这样做非常好.但是,如果你必须立即工作,睡眠可能是不可能的.
Java 9试图通过引入这个新方法来解决这个问题:
while(true) {
while(!newEmailArrived()) {
Thread.onSpinWait();
}
makeNotification();
}
Run Code Online (Sandbox Code Playgroud)
这与没有方法调用的情况完全相同,但系统可以自由地降低进程优先级; 当其他更重要的事情需要其资源时,减慢循环或减少此循环的电力.
我只想在阅读文档及其源代码后添加2美分.这种方法可能会引发一些优化,并且可能不-所以必须小心-你真的不能依靠它-因为这是一个hint到CPU超过需求,可能没有最初依靠任何不管怎样...这意味着,它的实际来源代码看起来像这样:
@HotSpotIntrinsicCandidate
public static void onSpinWait() {}
Run Code Online (Sandbox Code Playgroud)
什么这实际上意味着是,这种方法基本上是NO-OP,直至碰到c2 compiler的JIT,按照本
对于一个真实的例子,假设您要实现异步日志记录,即想要记录某些内容的线程不想等待其日志消息被“发布”(例如写入文件),只要最终成功了(因为他们有真正的工作要做。)
Producer(s):
concurrentQueue.push("Log my message")
Run Code Online (Sandbox Code Playgroud)
并说,您决定拥有一个专用的使用者线程,该线程专门负责将日志消息实际写入文件:
(Single)Consumer
while (concurrentQueue.isEmpty())
{
//what should I do?
}
writeToFile(concurrentQueue.popHead());
//loop
Run Code Online (Sandbox Code Playgroud)
问题是在while块内做什么?Java没有提供理想的解决方案:您可以执行Thread.sleep(),但是要花多长时间,这是很重的;或Thread.yield(),但未指定,或者您可以使用锁或互斥锁*,但这通常太重了,还会减慢生产者的速度(并消除了异步记录的既定目的)。
您真正想要的是对运行时说:“我希望等待的时间不会太长,但是我希望将对其他线程的等待/负面影响的开销降到最低”。那就是Thread.onSpinWait()进入的地方。
如上所示,在支持它的平台上(例如x86),onSpinWait()会被内化为PAUSE指令,这将为您带来所需的好处。所以:
(Single)Consumer
while (concurrentQueue.isEmpty())
{
Thread.onSpinWait();
}
writeToFile(concurrentQueue.popHead());
//loop
Run Code Online (Sandbox Code Playgroud)
它已经根据经验显示,这可“忙等待”式的循环改善延迟。
我还想澄清一下,它不仅对实现“自旋锁”有用(尽管在这种情况下肯定有用);上面的代码不需要任何类型的锁(旋转锁或其他锁)。
如果您想除草,您做不到英特尔的规格
*为了清楚起见,JVM在尝试最小化互斥锁的成本方面非常聪明,并且最初将使用轻量级锁,但这是另一种讨论。