发布于 2020-12-17 01:38:17
看来是这样的。我指的是OpenJDK wiki中关于织机中的阻塞操作的页面。它列出了对虚拟线程友好的操作中的Thread.sleep(),这意味着
当未固定时,它们将释放底层的载波线程,以便在操作阻塞时执行其他工作。
你接着问,
文档是稀疏的,并且不清楚实际存在的任何差异是否是有意的。不过,我倾向于认为,目标是使虚拟线程具有尽可能接近普通线程的语义。我怀疑会有足够聪明的程序来区分的方法,但如果有任何差异上升到“值得注意”的水平,那么我希望它们会被认为是bug。我的部分依据是推理,但我也向您介绍了织机状态文档在java.net上的应用,其中列出了它的“关键方法”
和
(强调后加)
发布于 2020-12-17 01:39:24
查看源代码,当您在虚拟线程上调用sleep(...)时,它由JVM的虚拟线程调度程序处理;也就是说,不直接执行syscall,也不阻塞本机线程。
所以:
在带有虚拟线程(光纤)的Project技术下,我们能以同样的方式使用
Thread.sleep吗?
是。
与休眠平台/内核线程相比,休眠虚拟线程有什么不同或值得注意的吗?
休眠虚拟线程的处理方式与您期望的虚拟线程的行为一样。其性能将不同于内核线程,但其行为被设计为透明的应用程序代码.这并不是对线程调度程序行为做出不必要的假设。
无论如何,Loom中用于Thread.sleep(...)的javadocs目前并没有提到内核和虚拟线程之间的任何区别。
发布于 2020-12-24 04:47:11
约翰·博林格的答复和斯蒂芬·C的回答都是正确的和信息丰富的。我想我应该添加一个代码示例来显示:
Thread.sleep。基准代码
让我们简单地写一个循环。在每个循环中,我们实例化一个Runnable来执行一个任务,并将该任务提交给一个遗嘱执行人服务。我们的任务是:做一些简单的数学运算,从System.nanoTime返回的System.nanoTime中减去。最后,我们将该数字打印到控制台。
但诀窍是,在计算之前,我们休眠执行该任务的线程。因为每个人都睡了最初的12秒,我们应该在至少12秒的死亡时间之后才能在控制台上看到任何东西。
然后提交的任务执行他们的工作。
通过启用/禁用一对注释行,我们以两种方式运行这一点。
ExecutorService executorService = Executors.newFixedThreadPool( 5 )
一个传统的线程池,使用了5个真正的核心(没有超线程)在这台Mac迷你(2018年)与3 GHz英特尔核心i5处理器和32 G内存。ExecutorService executorService = Executors.newVirtualThreadExecutor()
在这个早期访问Java 16的特殊构建中,Project提供了新的虚拟线程(光纤)支持的执行器服务。package work.basil.example;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TooFast
{
public static void main ( String[] args )
{
TooFast app = new TooFast();
app.demo();
}
private void demo ( )
{
System.out.println( "INFO - starting `demo`. " + Instant.now() );
long start = System.nanoTime();
try (
// 5 of 6 real cores, no hyper-threading.
ExecutorService executorService = Executors.newFixedThreadPool( 5 ) ;
//ExecutorService executorService = Executors.newVirtualThreadExecutor() ;
)
{
Duration sleep = Duration.ofSeconds( 12 );
int limit = 100;
for ( int i = 0 ; i < limit ; i++ )
{
executorService.submit(
new Runnable()
{
@Override
public void run ( )
{
try {Thread.sleep( sleep );} catch ( InterruptedException e ) {e.printStackTrace();}
long x = ( System.nanoTime() - 42 );
System.out.println( "x = " + x );
}
}
);
}
}
// With Project Loom, the flow-of-control blocks here until all submitted tasks have finished.
Duration demoElapsed = Duration.ofNanos( System.nanoTime() - start );
System.out.println( "INFO - demo took " + demoElapsed + " ending at " + Instant.now() );
}
}结果
结果令人震惊。
首先,在这两种情况下,在任何控制台活动之前,我们都会看到稍微超过12秒的延迟。因此,我们知道Thread.sleep是真正由平台/内核线程和虚拟线程执行的。
其次,虚拟线程只需几秒钟就能完成所有任务,而对于常规线程来说,则是分钟、小时或天。
有100项任务:
有1 000项任务:

有1 000 000项任务:
结论
使用常规线程,我们可以看到控制台上突然出现了几行重复的中断。因此,我们可以看到平台/内核线程在等待12秒钟的Thread.sleep到期时,实际上是如何被阻塞的。然后,所有五个线程都在大约同一时刻醒来,大约在同一时刻开始,每12秒,同时做他们的数学和写到控制台。这一行为得到证实,因为我们看到在https://en.wikipedia.org/wiki/List_of_macOS_components#Activity_Monitor应用程序中很少使用CPU核心。
顺便说一句:我假设主机操作系统注意到我们的Java线程实际上忙着什么都不做,然后使用它的CPU调度器在阻塞时挂起我们的Java线程,让其他进程(例如其他应用程序)使用CPU核心。但是如果是这样的话,这对JVM来说是透明的。从JVM的角度来看,休眠的Java线程在整个nap期间占用CPU。
对于虚拟线程,我们看到了截然不同的行为。Project的设计使得当一个虚拟线程阻塞时,JVM将该虚拟线程移出平台/内核线程,并将另一个虚拟线程放到它的位置。这种JVM内部线程交换比交换平台/内核线程便宜得多。承载这些不同虚拟线程的平台/内核线程可以保持忙碌,而不是等待每个块通过。
有关更多信息,请参见甲骨文织机项目的Ron最近(2020年末)的任何一次会谈,以及他在2020-05年度的论文https://cr.openjdk.java.net/%7Erpressler/loom/loom/sol1_part1.html?source=techstories.org。这种快速交换阻塞的虚拟线程的行为非常有效,以至于CPU可以一直处于忙碌状态。我们可以在Activity应用程序中确认这种效果。下面是活动监视器的屏幕截图,它使用虚拟线程运行百万任务。注意CPU内核在完成12秒的午睡后,实际上是100%繁忙的。

因此,所有的工作都是立即完成的,因为所有的100万个线程都在同时进行12秒钟的午睡,而平台/内核线程则是以5组的顺序进行午睡。在上面的屏幕截图中,我们看到了数百万任务的工作是如何在几秒钟内同时完成的,而平台/内核线程做同样数量的工作,但分散在几天内。
请注意,只有当任务经常被阻止时,才会出现这种戏剧性的性能提高。如果使用CPU绑定的任务,如视频编码,那么您应该使用平台/内核线程,而不是虚拟线程。大多数业务应用程序都存在很大的阻塞,例如等待对文件系统、数据库、其他外部服务或网络的调用来访问远程服务。虚拟线程在这种经常被阻塞的工作负载中发光。
https://stackoverflow.com/questions/65333188
复制相似问题