教程:外贸站群系统SEO效果怎么样

优采云 发布时间: 2022-10-02 04:06

  教程:外贸站群系统SEO效果怎么样

  外贸站群系统的SEO效果怎么样这篇文章是通过AI智能原创文章发布系统自动发布的。如果内容的联系影响了你的阅读,请理解,但是文章的知识点都是真实有效的。WXEE是一家专业从事外贸网站建设系统开发的科技型公司。通过技术研发,提高互联网行业的工作效率,降低运营成本。关于这篇文章的详细信息如下。

  wxeecms站群管理系统在搜索引擎SEO优化方面优势明显,集成了SEO优化中用到的工具、程序及相关设置。TDK设置:首页、栏目页、分类页、详情页可通过TKD自定义;

  

  TDK规则:对于分类页和详情页,TDK可以根据TDK规则批量设置,大大节省了TDK设置的工作量;Robots协议:在线编辑Robots.txt协议;生成sitemap.xml:在后台点击生成sitemap地图;搜索引擎自动推送:一键开启搜索引擎自动推广服务;自动提交搜索引擎采集;爬虫日志:可以查看各大搜索引擎的爬虫访问日志。

  无论是企业网站SEO优化所需的平台区域变电站系统,还是城市站群系统,通过wxeecms站群管理系统,区域变电站运营并且可以实现统一。管理和运营。

  Wxeecms站群管理系统集成了AI智能原创文章发布系统,可以定时定量发布网站SEO-optimized文章,自动添加关键词锚点链接,自动上传图片并添加ALT标签,自动收录百度,大大节省了SEO优化的人力成本,提高了SEO优化的效率。

  

  支持批量网站一键创建,或选择单个网站组的区域变电站。根据SEO优化的实际需要和用户喜好,可以选择二级域名或者二级目录的形式,可以实现批量区域网站SEO优化排名。二级域名和二级目录的主要区别在于:从搜索引擎的角度来看,二级域名相当于一个结果,一个独立的网站,一个独立的网站二级目录相当于同一网站不同的列类别。从搜索引擎的角度来看,二级目录的采集速度比较快,二级域名的采集被认为是一个独立的站点,所以它会更慢。从排名稳定性来看,二级域名的稳定性更强,因为网站首页的权重大于栏目页的权重,所以二级域名的排名会更稳定。

  你太厉害了,小AI的胡说八道文章你其实可以理解的,说明小AI还是很厉害的,或者你认为:外贸站群系统的SEO效果怎么样文章写错了,请给小爱点个赞。如果你喜欢我,我可以出现在你的网站上,永远听主人的话,为你服务。

  最新版本:JEP 尝鲜系列 3 - 使用虚线程进行同步网络 IO 的不阻塞原理

  相关JEP:

  使用虚拟线程进行网络 IO

  Project Loom 的主要目标是在 Java 平台上提供易于使用、高吞吐量、轻量级的并发以及 JVM 特性和 API 的新编程模型。这开辟了许多有趣和令人兴奋的前景,其中之一是简化网络交互的代码,同时牺牲性能。今天的服务器可以处理的打开套接字连接的数量远远超过它们可以支持的线程数量,这既是机遇也是挑战。

  但不幸的是,编写与网络交互的可扩展代码很困难。我们一般使用同步API进行编码,但是超过一定阈值后,同步代码会遇到瓶颈,难以扩展。因为这样的 API 在执行 I/O 操作时会阻塞,这反过来会绑定线程直到操作准备好,例如尝试从套接字读取数据但当前没有数据可读取时。当前线程,在 Java 平台中,是一种昂贵的资源,不能等待 I/O 操作完成才被释放。为了绕过这个限制,我们通常使用异步 I/O 或 Ractor 框架,因为它们可以构造在 I/O 操作中不绑定线程的代码,

  使用异步和非阻塞 API 比使用同步 API 更具挑战性,部分原因是使用这些 API 编写的代码是反人类的。同步 API 在很大程度上更容易使用;代码更容易编写、更容易阅读、更容易调试,而且堆栈上的大部分信息在调试时很有用。但如前所述,使用同步 API 的代码的可扩展性不如异步代码,因此我们必须做出艰难的选择:选择更简单的同步代码并接受它无法扩展的事实;或者选择更具可扩展性的异步代码,并处理所有复杂性。两者都不是一个好选择!Project Loom 的主要目的是使同步代码具有灵活性和可扩展性。

  在本文中,我们将了解在虚拟线程上调用 Java 平台的网络 API 是如何工作的。了解底层细节可以让我们更好、更自信地使用虚拟线程(纤程)。

  虚拟线程(光纤)

  在继续深入之前,我们需要了解 ProjectLoom 中的一个新线程——虚拟线程(也称为光纤)。

  虚拟线程是由 JVM 而非操作系统管理的用户模式线程。虚拟线程占用的系统资源很少,一个JVM可以容纳数百万个虚拟线程。特别适用于频繁执行、长时间阻塞、经常等待IO的任务。

  平台线程(即当前Java平台的线程)与操作系统内核线程一一对应。平台线程通常有一个非常大的堆栈,以及一些其他系统维护的资源。虚拟线程使用一小组平台线程作为载体线程。在虚拟线程中执行的代码通常不知道底层托管线程。锁和 I/O 操作是将主机线程从一个虚拟线程重新调度到另一个的调度点。虚拟线程可能被停放(例如 LockSupport.park() ),使其无法调度。可以取消停放的虚拟线程(例如 LockSupport.unpark(Thread)),从而重新启用其调度。

  网络 API

  Java 平台中有两个主要的网络 API:

  异步 - AsynchronousServerSocketChannel、AsynchronousSocketChannel 同步 - .Socket、.ServerSocket、.DatagramSocket、java.nio.channels.SocketChannel、java.nio.channels.ServerSocketChannel、java.nio.channels.DatagramChannel

  第一类异步 API,创建一个稍后启动的 I/O 操作,可以在启动 I/O 操作的线程以外的线程上完成。根据定义,这些 API 不会导致阻塞系统调用,因此在虚拟线程中运行时不需要特殊处理

  第二类同步 API,从它们在虚拟线程中运行时的行为的角度来看更有趣。在这些 API 中,可以将 NIO 通道相关配置为非阻塞模式。这种通道通常使用 I/O 事件通知机制来实现,例如注册到 Selector 以*敏*感*词*事件。与异步网络 API 类似,在虚拟线程中执行不需要额外的处理,因为 I/O 操作本身不会调用阻塞系统调用,这些调用留给 Selector。最后我们来看看阻塞模式下通道的配置以及相关的API(我们这里称这个API为同步阻塞API)。同步 API 的语义要求 I/O 操作一旦启动,在调用线程中完成或失败,然后将控制权返回给调用者。但是如果 I/O 操作“未准备好”怎么办?例如,当前没有要读取的数据。

  同步阻塞 API

  在虚拟线程中运行的 Java 同步网络 API 将底层本机套接字切换到非阻塞模式。当 Java 代码发起一个 I/O 请求并且请求没有立即完成时(本机套接字返回 EAGAIN - 这意味着“未准备好”/“将阻塞”),底层套接字注册到 JVM 内部事件通知机制(轮询),并且虚拟线程将被停放。当底层 I/O 操作准备好(相关事件将到达 Poller)时,虚拟线程将被 unpark,并重试底层 Socket 操作。

  让我们通过一个例子来仔细看看这个原理。首先,我们需要下载项目loom的JDK(地址:),解压后使用。

  接下来写代码:

  //Java 16 中的 Record 对象,可以理解为有包含两个 final 属性(url 和 response)的类

static record URLData (URL url, byte[] response) { }

static List retrieveURLs(URL... urls) throws Exception {

//创建虚拟线程线程池

try (var executor = Executors.newVirtualThreadExecutor()) {

//生成读取对每个 url 执行 getURL 方法的任务

var tasks = Arrays.stream(urls)

.map(url -> (Callable)() -> getURL(url))

.toList();

//提交任务,等待并返回所有结果

return executor.submit(tasks)

.filter(Future::isCompletedNormally)

.map(Future::join)

.toList();

}

}

//读取url的内容

static URLData getURL(URL url) throws IOException {

try (InputStream in = url.openStream()) {

return new URLData(url, in.readAllBytes());

}

}

public static void main(String[] args) throws Exception {

//访问 google,由于你懂得,会比较慢

List urlData = retrieveURLs(new URL("https://www.google.com/"));

}

  我们使用retrieveURLs来访问谷歌,这肯定很慢,以确保采集到堆栈。同时jstack采集栈不能使用(目前jstack采集无法到达虚拟线程栈,只能采集去承载线程的栈),需要在 jcmd 命令采集 中使用 JavaThread.dump jps

25496 LoomThreadMain

12512 Jps

> jcmd 25496 JavaThread.dump threads.txt -overwrite

  然后继续执行程序,再执行命令,采集虚拟线程进行I/O操作时的栈:

  > jcmd 25496 JavaThread.dump threads2.txt -overwrite

  我们查看文件threads.txt,其中我们关心的线程信息是:

  "main" #1

java.base@17-loom/jdk.internal.misc.Unsafe.park(Native Method)

java.base@17-loom/java.util.concurrent.locks.LockSupport.park(LockSupport.java:371)

java.base@17-loom/java.util.concurrent.LinkedTransferQueue$Node.block(LinkedTransferQueue.java:470)

java.base@17-loom/java.util.concurrent.ForkJoinPool.unmanagedBlock(ForkJoinPool.java:3470)

java.base@17-loom/java.util.concurrent.ForkJoinPool.managedBlock(ForkJoinPool.java:3441)

java.base@17-loom/java.util.concurrent.LinkedTransferQueue.awaitMatch(LinkedTransferQueue.java:669)

java.base@17-loom/java.util.concurrent.LinkedTransferQueue.xfer(LinkedTransferQueue.java:616)

java.base@17-loom/java.util.concurrent.LinkedTransferQueue.take(LinkedTransferQueue.java:1286)

java.base@17-loom/java.util.concurrent.ExecutorServiceHelper$BlockingQueueSpliterator.tryAdvance(ExecutorServiceHelper.java:197)

java.base@17-loom/java.util.Spliterator.forEachRemaining(Spliterator.java:326)

java.base@17-loom/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)

java.base@17-loom/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)

java.base@17-loom/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:550)

java.base@17-loom/java.util.stream.AbstractPipeline.evaluateToArrayNode(AbstractPipeline.java:260)

java.base@17-loom/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:616)

java.base@17-loom/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:622)

java.base@17-loom/java.util.stream.ReferencePipeline.toList(ReferencePipeline.java:627)

app//com.github.hashjang.LoomThreadMain.retrieveURLs(LoomThreadMain.java:43)

<p>

app//com.github.hashjang.LoomThreadMain.main(LoomThreadMain.java:29)

"ForkJoinPool-1-worker-1" #27

java.base@17-loom/java.lang.Continuation.run(Continuation.java:300)

java.base@17-loom/java.lang.VirtualThread.runContinuation(VirtualThread.java:240)

java.base@17-loom/java.lang.VirtualThread$$Lambda$25/0x0000000801053fc0.run(Unknown Source)

java.base@17-loom/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1395)

java.base@17-loom/java.util.concurrent.ForkJoinTask.doExec$$$capture(ForkJoinTask.java:373)

java.base@17-loom/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java)

java.base@17-loom/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1177)

java.base@17-loom/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1648)

java.base@17-loom/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1615)

java.base@17-loom/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165)

"" #26 virtual

java.base/java.util.concurrent.ConcurrentHashMap.transfer(ConcurrentHashMap.java:2431)

java.base/java.util.concurrent.ConcurrentHashMap.addCount(ConcurrentHashMap.java:2354)

java.base/java.util.concurrent.ConcurrentHashMap.putVal(ConcurrentHashMap.java:1075)

java.base/java.util.concurrent.ConcurrentHashMap.putIfAbsent(ConcurrentHashMap.java:1541)

java.base/sun.util.locale.LocaleObjectCache.get(LocaleObjectCache.java:68)

java.base/java.util.Locale.getInstance(Locale.java:841)

java.base/java.util.Locale.forLanguageTag(Locale.java:1736)

java.base/sun.util.locale.provider.LocaleProviderAdapter.toLocaleArray(LocaleProviderAdapter.java:323)

java.base/sun.util.locale.provider.CalendarDataProviderImpl.getAvailableLocales(CalendarDataProviderImpl.java:63)

java.base/java.util.spi.LocaleServiceProvider.isSupportedLocale(LocaleServiceProvider.java:217)

java.base/sun.util.locale.provider.LocaleServiceProviderPool.findProviders(LocaleServiceProviderPool.java:306)

java.base/sun.util.locale.provider.LocaleServiceProviderPool.getLocalizedObjectImpl(LocaleServiceProviderPool.java:274)

java.base/sun.util.locale.provider.LocaleServiceProviderPool.getLocalizedObject(LocaleServiceProviderPool.java:256)

java.base/sun.util.locale.provider.CalendarDataUtility.retrieveFirstDayOfWeek(CalendarDataUtility.java:76)

java.base/java.util.Calendar.setWeekCountData(Calendar.java:3419)

java.base/java.util.Calendar.(Calendar.java:1612)

java.base/java.util.GregorianCalendar.(GregorianCalendar.java:738)

java.base/java.util.Calendar$Builder.build(Calendar.java:1494)

java.base/sun.util.locale.provider.CalendarProviderImpl.getInstance(CalendarProviderImpl.java:87)

java.base/java.util.Calendar.createCalendar(Calendar.java:1697)

java.base/java.util.Calendar.getInstance(Calendar.java:1661)

java.base/java.text.SimpleDateFormat.initializeCalendar(SimpleDateFormat.java:680)

java.base/java.text.SimpleDateFormat.(SimpleDateFormat.java:624)

java.base/java.text.SimpleDateFormat.(SimpleDateFormat.java:603)

java.base/sun.security.util.DisabledAlgorithmConstraints$DenyAfterConstraint.(DisabledAlgorithmConstraints.java:695)

java.base/sun.security.util.DisabledAlgorithmConstraints$Constraints.(DisabledAlgorithmConstraints.java:424)

java.base/sun.security.util.DisabledAlgorithmConstraints.(DisabledAlgorithmConstraints.java:149)

java.base/sun.security.ssl.SSLAlgorithmConstraints.(SSLAlgorithmConstraints.java:49)

java.base/sun.security.ssl.ProtocolVersion.(ProtocolVersion.java:158)

java.base/sun.security.ssl.ProtocolVersion.(ProtocolVersion.java:41)

java.base/sun.security.ssl.SSLContextImpl$AbstractTLSContext.(SSLContextImpl.java:539)

java.base/java.lang.Class.forName0(Native Method)

java.base/java.lang.Class.forName(Class.java:375)

java.base/java.security.Provider$Service.getImplClass(Provider.java:1937)

java.base/java.security.Provider$Service.getDefaultConstructor(Provider.java:1968)

java.base/java.security.Provider$Service.newInstanceOf(Provider.java:1882)

java.base/java.security.Provider$Service.newInstanceUtil(Provider.java:1890)

java.base/java.security.Provider$Service.newInstance(Provider.java:1865)

java.base/sun.security.jca.GetInstance.getInstance(GetInstance.java:236)

java.base/sun.security.jca.GetInstance.getInstance(GetInstance.java:164)

java.base/javax.net.ssl.SSLContext.getInstance(SSLContext.java:184)

java.base/javax.net.ssl.SSLContext.getDefault(SSLContext.java:110)

java.base/javax.net.ssl.SSLSocketFactory.getDefault(SSLSocketFactory.java:83)

java.base/javax.net.ssl.HttpsURLConnection.getDefaultSSLSocketFactory(HttpsURLConnection.java:334)

java.base/javax.net.ssl.HttpsURLConnection.(HttpsURLConnection.java:291)

java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.(HttpsURLConnectionImpl.java:81)

java.base/sun.net.www.protocol.https.Handler.openConnection(Handler.java:62)

java.base/sun.net.www.protocol.https.Handler.openConnection(Handler.java:57)

java.base/java.net.URL.openConnection(URL.java:1093)

java.base/java.net.URL.openStream(URL.java:1159)

com.github.hashjang.LoomThreadMain.getURL(LoomThreadMain.java:48)

com.github.hashjang.LoomThreadMain.lambda$retrieveURLs$0(LoomThreadMain.java:38)

java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:295)

java.base/java.util.concurrent.FutureTask.run(FutureTask.java)

java.base/java.util.concurrent.ThreadExecutor$TaskRunner.run(ThreadExecutor.java:385)

java.base/java.lang.VirtualThread.run(VirtualThread.java:295)

java.base/java.lang.VirtualThread$VThreadContinuation.lambda$new$0(VirtualThread.java:172)

java.base/java.lang.Continuation.enter0(Continuation.java:372)

java.base/java.lang.Continuation.enter(Continuation.java:365)</p>

  其中“”#26 virtual是我们程序中创建的虚拟线程,从栈中可以看出该虚拟线程还没有进行I/O操作。从线程栈也可以看出,这个虚拟线程的承载线程是“ForkJoinPool-1-worker-1”#27. 可以看出,虚拟线程的默认承载线程是普通的ForkJoinPool 将在线程中 Java 8 之后默认启动。而虚拟线程的工作是通过Continuation类来完成的。

  查看线程2.txt:

  "main" #1

java.base@17-loom/jdk.internal.misc.Unsafe.park(Native Method)

<p>

java.base@17-loom/java.util.concurrent.locks.LockSupport.park(LockSupport.java:371)

java.base@17-loom/java.util.concurrent.LinkedTransferQueue$Node.block(LinkedTransferQueue.java:470)

java.base@17-loom/java.util.concurrent.ForkJoinPool.unmanagedBlock(ForkJoinPool.java:3470)

java.base@17-loom/java.util.concurrent.ForkJoinPool.managedBlock(ForkJoinPool.java:3441)

java.base@17-loom/java.util.concurrent.LinkedTransferQueue.awaitMatch(LinkedTransferQueue.java:669)

java.base@17-loom/java.util.concurrent.LinkedTransferQueue.xfer(LinkedTransferQueue.java:616)

java.base@17-loom/java.util.concurrent.LinkedTransferQueue.take(LinkedTransferQueue.java:1286)

java.base@17-loom/java.util.concurrent.ExecutorServiceHelper$BlockingQueueSpliterator.tryAdvance(ExecutorServiceHelper.java:197)

java.base@17-loom/java.util.Spliterator.forEachRemaining(Spliterator.java:326)

java.base@17-loom/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)

java.base@17-loom/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)

java.base@17-loom/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:550)

java.base@17-loom/java.util.stream.AbstractPipeline.evaluateToArrayNode(AbstractPipeline.java:260)

java.base@17-loom/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:616)

java.base@17-loom/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:622)

java.base@17-loom/java.util.stream.ReferencePipeline.toList(ReferencePipeline.java:627)

app//com.github.hashjang.LoomThreadMain.retrieveURLs(LoomThreadMain.java:43)

app//com.github.hashjang.LoomThreadMain.main(LoomThreadMain.java:29)

"ForkJoinPool-1-worker-1" #25

java.base@17-loom/jdk.internal.misc.Unsafe.park(Native Method)

java.base@17-loom/java.util.concurrent.locks.LockSupport.parkUntil(LockSupport.java:449)

java.base@17-loom/java.util.concurrent.ForkJoinPool.awaitWork(ForkJoinPool.java:1719)

java.base@17-loom/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1616)

java.base@17-loom/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165)

"Read-Poller" #41

java.base@17-loom/sun.nio.ch.WEPoll.wait(Native Method)

java.base@17-loom/sun.nio.ch.WEPollPoller.poll(WEPollPoller.java:64)

java.base@17-loom/sun.nio.ch.Poller.poll(Poller.java:196)

java.base@17-loom/sun.nio.ch.Poller.lambda$startPollerThread$0(Poller.java:66)

java.base@17-loom/sun.nio.ch.Poller$$Lambda$89/0x00000008010e5168.run(Unknown Source)

java.base@17-loom/java.lang.Thread.run(Thread.java:1521)

java.base@17-loom/jdk.internal.misc.InnocuousThread.run(InnocuousThread.java:161)

"" #24 virtual

java.base/java.lang.Continuation.yield(Continuation.java:402)

java.base/java.lang.VirtualThread.yieldContinuation(VirtualThread.java:367)

java.base/java.lang.VirtualThread.park(VirtualThread.java:534)

java.base/java.lang.System$2.parkVirtualThread(System.java:2373)

java.base/jdk.internal.misc.VirtualThreads.park(VirtualThreads.java:60)

java.base/sun.nio.ch.NioSocketImpl.park(NioSocketImpl.java:184)

java.base/sun.nio.ch.NioSocketImpl.park(NioSocketImpl.java:212)

java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:607)

java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:331)

java.base/java.net.Socket.connect(Socket.java:642)

java.base/sun.security.ssl.SSLSocketImpl.connect(SSLSocketImpl.java:299)

java.base/sun.security.ssl.BaseSSLSocketImpl.connect(BaseSSLSocketImpl.java:174)

java.base/sun.net.NetworkClient.doConnect(NetworkClient.java:182)

java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:497)

java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:600)

java.base/sun.net.www.protocol.https.HttpsClient.(HttpsClient.java:266)

java.base/sun.net.www.protocol.https.HttpsClient.New(HttpsClient.java:380)

java.base/sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.getNewHttpClient(AbstractDelegateHttpsURLConnection.java:189)

java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1232)

java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:1120)

java.base/sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:175)

java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1653)

java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1577)

java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:224)

java.base/java.net.URL.openStream(URL.java:1159)

com.github.hashjang.LoomThreadMain.getURL(LoomThreadMain.java:48)

com.github.hashjang.LoomThreadMain.lambda$retrieveURLs$0(LoomThreadMain.java:38)

java.base/java.util.concurrent.FutureTask.run(FutureTask.java:295)

java.base/java.util.concurrent.ThreadExecutor$TaskRunner.run(ThreadExecutor.java:385)

java.base/java.lang.VirtualThread.run(VirtualThread.java:295)

java.base/java.lang.VirtualThread$VThreadContinuation.lambda$new$0(VirtualThread.java:172)

java.base/java.lang.Continuation.enter0(Continuation.java:372)

java.base/java.lang.Continuation.enter(Continuation.java:365)</p>

  从这里的线程栈可以看出,当虚拟线程进行I/O操作时,会调用java.lang.Continuation.yield来放弃承载线程的资源(相当于park living)。查看原创托管线程“ForkJoinPool-1-worker-1”#25,确实是空闲的。那么 I/O 操作去了哪里?这导致了这个线程“Read-Poller”#41。

  该线程是 JVM 共享读取轮询器。它的核心逻辑是执行一个基本的事件循环,*敏*感*词*所有同步网络读(ready network read)、connect(准备发起网络连接)和accept(准备接受网络连接)操作。当这些 I/O 操作准备好后,会通知 poller,unpark 对应的虚拟线程会继续执行。同时,除了 read poller,还有一个 write poller 用于写事件。

  我是用windows来测试的,windows中poller的底层实现是基于wepoll的,所以我们看到栈中收录了WEPoll。MacOS 的 kqueue,Linux 的 epoll

  轮询器维护一个由虚拟线程的文件描述符作为键的映射。当一个虚拟线程向轮询器注册它的文件描述符时,它使用虚拟线程的文件描述符作为键和虚拟线程本身作为映射的值。当poller的事件循环中的相关事件准备好后,通过事件中的虚拟线程文件描述符在map中找到对应的虚拟线程unpark。

  可扩展性

  如果简单的看,上面的设计和使用 NIO Channel 和 Selector 并没有太大区别,在很多服务器端的框架和库中都可以找到,比如 Netty。但相对而言,NIO Channel 和 Selector 提供了更复杂的模型,用户代码必须实现事件循环并跨 I/O 边界维护应用程序逻辑,而虚拟线程提供更简单直观的编程模型,Java 平台负责调度任务并跨 I/O 边界维护相应的上下文。

  正如我们之前看到的,虚拟线程的默认宿主线程是 ForkJoinPool。这是一个非常适合虚拟线程的线程池,工作窃取算法可以将虚拟线程调度运行到极致。

  综上所述

  同步 Java 联网 API 已经重新实现,相关的 JEP 包括 JEP 353 和 JEP 373. 在虚拟线程中运行时,不能立即完成的 I/O 操作会导致虚拟线程被停顿。当 I/O 准备好时,虚拟线程将被释放。与目前的异步非阻塞I/O实现代码相比,这种实现更加简单易用,并且隐藏了很多业务不关心的实现细节。

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线