Java新结构化并行形式指南 (java new)
译者|布加迪
结构化并发是中经常使用多线程的一种新形式。它准许开发人员在充沛应用传统线程和虚构线程的同时思考逻辑组中的上班。结构化并收回如今Java21的预览版中,它是选择Java未来的一个关键方面,所以如今是开局经常使用它的好机遇。
为什么咱们须要结构化并发?
编写并发软件是软件开发者面临的最大应战之一。Java的线程形式使其成为并发言语中的有力竞争者,然而多线程不时天生很辣手。结构化并发准许您使用具备结构化编程语法的多线程。实质上,它提供了一种经常使用相熟的程序流程和构件编写并发软件的方法。这让开发者可以专一于手头的事务,而不是线程编排。正如结构化并发性的JEP所说:假设一个义务分红并发子义务,它们都回到相反的位置,即义务的代码块。
虚构线程如今是Java的一项正式个性,它可以低老本生成线程,从而取得并发性能。结构化并发提供了这么做的繁难语法。因此,Java如今有了一个共同的、高度优化的线程系统,而且易于了解。
新的StructuredTaskScope类
结构化并发中的关键类是java.util.concurrent.StructuredTaskScope。Java21文档蕴含如何经常使用结构化并发的示例。截止本文发稿时,您须要经常使用--enable-preview和--source21或--source22来启用Java程序中的结构化并发。我的$java--version是openjdk22-ea,所以咱们经常使用Maven的示例将为编译步骤指定--enable-preview--source22,为口头步骤指定--enable-preview。留意,SDKMan关于治理多个JDK装置是一个很好的选用。
您可以在本文的代码存储库中找到示例代码。留意为口头设置—enable-preview的.mvn/jvm.config文件。若要运转代码,经常使用$mvncleancompileexec:java。
具备结构化并发的多线程
就本文示例而言,咱们将向StarWarsAPI(SWAPI)收回几个恳求,经过行星的ID失掉无关行星的消息。假设咱们在规范的同步Java中口头此操作,或许会经常使用HTTPClient口头相似代码片段1的操作。
代码片段1.相似传统方法的多API调用
复制
packagecom.infoworld;
importorg.apache.http.client.methods.CloseableHttpResponse;
importorg.apache.http.client.methods.HttpGet;
importorg.apache.http.impl.client.CloseableHttpClient;
importorg.apache.http.impl.client.HttpClients;
importorg.apache.http.util.EntityUtils;
publicclass{
publicStringgetPl(intplanetId)throwsException{
System.out.println("BEGINgetPlanet()");
+planetId+"/";
CloseableHttpClienthttpClient=HttpClients.createDefault();
HttpGetrequest=newHttpGet(url);
CloseableHttpResponseresponse=httpClient.execute(request);
//Checktheresponsestatuscode
if(response.getStatusLine().getStatusCode()!=200){
System.err.println("ErrorfetchingplanetinformationforID:"+planetId);
thrownewRuntimeException("ErrorfetchingplanetinformationforID:"+planetId);
//ParsetheJSONresponseandextractplanetinformation
ret=EntityUtils.toString(response.getEntity());
System.out.println("GotaPlanet:"+ret);
//ClosetheHTTPresponseandclient
response.close();
httpClient.close();
returnret;
voidsync()throwsException{
int[]planetIds={1,2,3,4,5};
for(intplanetId:planetIds){
getPlanet(planetId);
publicstaticvoidmn(String[]args){
varmyApp=newApp();
System.out.println("nr--BEGINSync");
myApp.sync();
}catch(Exceptione){
System.err.println("Error:"+e);
在代码片段1中,咱们有一个调用sync()方法的主方法,该方法在调用端点时,只是对一组ID启动迭代处置。这些调用经过getPlanet()方法收回,该方法经常使用ApacheHTTP库来处置样板恳求、照应和失误处置。实践上,该方法接纳每个照应,假设正确(200),输入到控制台,否则抛出失误。(这些示例经常使用了起码的失误,所以在这种状况下咱们只抛出RuntimeException。)
输入是这样的:
复制
--BEGINSync
BEGINgetPlanet()
GotaPlanet:{"name":"Tatooine"}
BEGINgetPlanet()
GotaPlanet:{"name":"Alderaan"}
BEGINgetPlanet()
GotaPlanet:{"name":"Yavin}
BEGINgetPlanet()
GotaPlanet:{"name":"Hoth"}
BEGINgetPlanet()
GotaPlanet:{"name":"Dagobah"}
如今无妨经常使用结构化并发尝试同一个示例。如代码片段2所示,结构化并发准许咱们将调用分解成并发恳求,并将一切内容放在相反的代码空间中。在代码片段2中,咱们参与了必要的StructuredTaskScope导入,而后经常使用其外围方法fork()和join(),将每个恳求分解成各自的线程,而后期待它们所有成功。
代码片段2.经常使用StructuredTaskScopeNow的多API调用
复制
packagecom.infoworld;
importjava.util.concurrent.*;
importjava.util.concurrent.StructuredTaskScope.*;
publicclassApp{
publicStringgetPlanet(intplanetId)throwsException{
//...same...
voidsync()throwsException{
int[]planetIds={1,2,3,4,5};
for(intplanetId:planetIds){
getPlanet(planetId);
voidsc()throwsException{
int[]planetIds={1,2,3,4,5};
try(varscope=newStructuredTaskScope<Object>()){
for(intplanetId:planetIds){
scope.fork(()->getPlanet(planetId));
scope.join();
}catch(Exceptione){
System.out.println("Error:"+e);
publicstaticvoidmain(String[]args){
varmyApp=newApp();
System.out.println("nr--BEGINStructuredConcurrency");
myApp.sc();
}catch(Exceptione){
System.err.println("Error:"+e);
假设咱们运转代码片段2,将失掉相似的输入,但速度要快不少,这是由于恳求是同时收回、并发启动的。无妨思考sc()方法(经常使用多线程)与sync()方法(经常使用同步代码)之间的区别。结构化并发方法没有构想的那么难,提供结果的速度却快得多。
处置义务和子义务
自动状况下,StructuredTaskScope被创立时,它经常使用虚构线程,所以咱们实践上并没有在这里性能操作系统线程;相反,咱们通知JVM以最有效的形式编排恳求。(StructuredTaskScope的结构函数也接受ThreadFactory。)
在代码片段2中,咱们在try-with-resource块中创立StructuredTaskScope对象,这是它原本的经常使用形式。咱们可以经常使用fork()创立恣意数量的作业。fork()方法接受任何成功Callable的程序,也就是说,任何方法或函数。这里,咱们将getPlanet()方法包装在一个匿名函数中:()->getPlanet(planetId)——这是一种向指标函数传递参数的适用语法。
当咱们调用join()时,咱们通知作用域期待一切被分叉的作业。实质上,join()将咱们带回到同步形式。分叉的作业将依照TaskScope的性能启动处置。
封锁义务作用域
由于咱们在try-with-resource块中创立了TaskScope,因此当该块完结时,作用域将智能封锁。这为作用域调用shutdown()进程,作用域可以定制,以便依据须要来处置运转中线程的处置。假设须要在作用域封锁之前封锁它,也可以手动调用shutdown()方法。
StructuredTaskScope包括两个成功内置封锁战略的类:ShutDownOnSuccess和ShutDownOnFailure。这些类监督成功或出错的子义务,而后敞开其他运转中的线程。经常使用目前的设置,咱们可以这样经常使用这些类:
代码片段3.内置封锁战略
复制
voidfailFast()throwsExecutionException,InterruptedException{
int[]planetIds={1,2,3,-1,4};
try(varscope=newStructuredTaskScope.ShutdownOnFailure()){
for(intplanetId:planetIds){
scope.fork(()->getPlanet(planetId));
scope.join();
voidsucceedFast()throwsExecutionException,InterruptedException{
int[]planetIds={1,2};
try(varscope=newStructuredTaskScope.ShutdownOnSuccess()){
for(intplanetId:planetIds){
scope.fork(()->getPlanet(planetId));
scope.join();
}catch(Exceptione){
System.out.println("Error:"+e);
publicstaticvoidmain(String[]args){
varmyApp=newApp();
System.out.println("nr--BEGINsucceedFast");
myApp.succeedFast();
}catch(Exceptione){
System.out.println(e.getMessage());
System.out.println("nr--BEGINfailFast");
myApp.failFast();
}catch(Exceptione){
System.out.println(e.getMessage());
这些战略将给出相似以下的输入:
复制
--BEGINsucceedFast
BEGINgetPlanet()
BEGINgetPlanet()
GotaPlanet:{"name":"Alderaan"}
org.apache.http.impl.execchain.RetryExecexecute
INFO:I/Oexception(java.net.SocketException)caughtwhenprocessingrequestto{s}->
--BEGINfailFast
BEGINgetPlanet()
BEGINgetPlanet()
BEGINgetPlanet()
BEGINgetPlanet()
BEGINgetPlanet()
GotaPlanet:{"name":"Hoth"}
GotaPlanet:{"name":"Tatooine"}
ErrorfetchingplanetinformationforID:-1
org.apache.http.impl.execchain.RetryExecexecute
INFO:I/Oexception(java.net.SocketException)caughtwhenprocessingrequestto{s}->
因此,咱们领有的是一种繁难的机制,可以并发启动一切恳求,而后在一个恳求成功或失败时敞开其他的恳求。这里,可以启动任何定制。结构化并发文档包括一个示例,在子义务成功或失败时搜集子义务结果,而后前往结果。这很容易成功,只有经过笼罩join()方法,并观察每个义务的结果。
StructuredTaskScope.Subtask
在咱们的示例中没有看到的一件事是观察子义务的前往值。每次StructuredTaskScope.fork()被调用时,就前往StructuredTaskScope.SubTask对象。咱们可以应用它来观察义务的形态。比如在sc()方法中,咱们可以这么做:
代码片段4.经常使用StructuredTaskScope.Subtask观察形态
复制
importjava.util.concurrent.StructuredTaskScope.Subtask;
importjava.util.ArrayList;
voidsc()throwsException{
int[]planetIds={1,2,3,4,5};
ArrayList<Subtask>tasks=newArrayList<Subtask>(planetIds.length);
try(varscope=newStructuredTaskScope<Object>()){
for(intplanetId:planetIds){
tasks.add(scope.fork(()->getPlanet(planetId)));
scope.join();
}catch(Exceptione){
System.out.println("Error:"+e);
for(Subtaskt:tasks){
System.out.println("Task:"+t.state());
在这个示例中,咱们将每个义务保留在ArrayList中,而后在启动join()操作之后输入它们的形态。留意,Subtask的可用形态被定义为enum。这个新方法将输入相似以下的内容:
复制
--BEGINStructuredConcurrency
BEGINgetPlanet()
BEGINgetPlanet()
BEGINgetPlanet()
BEGINgetPlanet()
BEGINgetPlanet()
GotaPlanet:{"name":"Dagobah"}
GotaPlanet:{"name":"Hoth"}
GotaPlanet:{"name":"Tatooine"}
GotaPlanet:{"name":"YavinIV"}
GotaPlanet:{"name":"Alderaan"}
Task:SUCCESS
Task:SUCCESS
Task:SUCCESS
Task:SUCCESS
Task:SUCCESS
论断
在虚构线程和结构化并发之间,Java开发者领有一种有目共睹的新机制,可以将简直一切代码分解成并发义务,不会有太大的开支。高低文和需求很关键,所以不要仅仅由于存在这些新的并发工具就经常使用它们。与此同时,这种组合确实提供了一些弱小的力气。一旦您遇到产生许多义务的瓶颈时,您可以轻松地将它们所有交给虚构线程引擎,该引擎将找到编排它们的最佳方法。具备结构化并发的新线程形式还使您易于定制和微调这种行为。
至于开发者未来如何在咱们的运行程序、框架和主机中经常使用这些新的并发性能,值得咱们刮目相待。
小常识:结构化并发中的线程树
结构化并发包括对调试和了解线程之间相关的支持。特意是,结构化并发将一切线程关联到树结构中,作用域位于根。这样一来,检查线程之间的相关就变得很繁难,即使经常使用嵌套作用域也是如此。说明文档提供了一个好的示例,标明如何经常使用Java诊断命令(jcmd)适用程序,将线程的运转时规划转储到控制台。
原文题目: GetstartedwithJava'snewstructuredconcurrencymodel ,作者:MatthewTyson
反映java程序并行机制特点是
Java程序并行机制的特点主要包括以下几个方面:
1. 多线程编程:Java提供了强大的多线程支持,允许开发者在单个程序中创建多个执行线程,从而实现并行处理。这使得Java程序能够同时执行多个任务,大大提高了程序的效率。
2. 线程安全:Java对线程安全有很好的支持,包括线程的创建、启动、同步、通信等操作。开发者无需担心线程间的同步问题,因为Java提供了内置的线程同步机制,如锁和条件变量等。
3. 丰富的API支持:Java提供了丰富的API来支持多线程编程,包括Thread类、Runnable接口、Callable接口等。这些API提供了创建、启动、控制和同步线程的工具和方法。
4. 任务并行:Java中的多线程机制允许开发者将一个任务分解为多个子任务,每个子任务可以在单独的线程中并行执行。这种任务级别的并行处理可以提高程序的执行效率。
5. 高效的任务调度:Java的并发包()提供了高效的线程调度机制,能够根据系统的负载情况动态调整线程的数量,以实现最佳的资源利用率。
6. 异步非阻塞操作:Java的多线程机制支持异步非阻塞的操作,即一个线程可以等待另一个线程完成任务,而不需要等待该线程完成。这种机制可以提高程序的响应速度和效率。
7. 性能优化:Java提供了多种性能优化的工具和方法,如使用懒加载、减少锁的持有时间、使用更高效的算法等,以提高并行程序的性能和效率。
以上就是Java程序并行机制的特点,通过这些特点,开发者可以充分利用多核CPU的资源,提高程序的执行效率。同时,Java的多线程机制也提供了一种简单而有效的方式,来实现并发和并行编程。
哪位大佬有 并行编程模式,帮忙找一下书籍百度网盘资源呗!
我这里有您想要的资源,通过网络网盘免费分享给您:
并行编程模式=PATTERNS FOR PARALLEL PROGRAMMING_
免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。