如何在结构化并发下运行JDK 19?

11

我已经编辑了内容,以便使它与原始帖子保持最新状态。

我想尝试新的Project Loom功能,该功能定义在:JEP 428: Structured Concurrency (Incubator)

我的pom.xml文件中已经有了这个功能。

<properties>
  <maven.compiler.executable>${env.JAVA_HOME}/bin/javac</maven.compiler.executable>
  <maven.compiler.source>19</maven.compiler.source>
  <maven.compiler.target>19</maven.compiler.target>
</properties>

. . .

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <version>3.10.1</version>
  <configuration>
    <compilerArgs>
      <arg>--add-modules=jdk.incubator.concurrent</arg>
      <arg>--enable-preview</arg>
    </compilerArgs>
  </configuration>
</plugin>

如果JAVA_HOME指向JDK 19,但我尝试通过mvn compile构建时出现错误。

[ERROR] C:\Users\ERIC\Documents\git\loom-lab\laboratory\src\main\java\net\kolotyluk\loom\Structured.java:3:20:  error: package jdk.incubator.concurrent is not visible
[ERROR] C:\Users\ERIC\Documents\git\loom-lab\laboratory\src\main\java\net\kolotyluk\loom\Structures.java:3:20:  error: package jdk.incubator.concurrent is not visible
. . .

很多人帮我解决了这个问题,显然他们做到了,但由于某些原因,我无法使mvn compile工作。

然而,在IntelliJ下我可以编译和运行代码。之前我从未遇到过在能让IntelliJ编译的情况下无法让Maven编译的问题。通常情况是相反的。


3
猜测:也许您需要一个包含“requires”行的module-info.java文件,如此处所讨论的那样(https://dev59.com/_cTra4cB1Zd3GeqP20rd#72072054)。 - Basil Bourque
3
参见JEP 11:孵化器模块:“类路径上的应用程序必须使用--add-modules命令行选项请求解析孵化器模块。作为模块开发的应用程序可以直接指定 requires 或 requires transitive 依赖关系模块。” 因此,您需要包含--add-modules参数或具有requires指令,但不能同时使用。您可以在命令行上(即使用javacjava)使其正常工作吗?也许Maven没有按照您的期望执行。 - Slaw
2
如果您在POM文件中将其更改为--add-modules=jdk.incubator.concurrent(带有=),会怎样呢? - Slaw
请注意,该模块需要在编译时和运行时都存在。 - dan1st
3个回答

5

module-info.java文件

我不完全理解所有的细节,但我成功地访问了 Java 19 中预览的 Project Loom 特性,包括 虚拟线程结构化并发

以下是我的 main 方法。

record Event( UUID id , Instant when , Integer reading ) {}

try ( var scope = new StructuredTaskScope.ShutdownOnFailure() )
{
    Future < UUID > futureId = scope.fork( ( ) -> UUID.randomUUID() );
    Future < Instant > futureWhen = scope.fork( ( ) -> Instant.now() );
    Future < Integer > futureReading = scope.fork( ( ) -> ThreadLocalRandom.current().nextInt( 1 , 10 ) );

    scope.join();           // Join both forks
    scope.throwIfFailed();  // ... and propagate errors
    Event event = new Event( futureId.get() , futureWhen.get() , futureReading.get() );
    System.out.println( event );
}
catch ( InterruptedException e )
{
    throw new RuntimeException( e );
}
catch ( ExecutionException e )
{
    throw new RuntimeException( e );
}

那段代码使用了这些导入:

import java.time.Instant;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadLocalRandom;

当运行时:

事件[id=de316aca-10b1-41ed-bfc6-732c4e184566, when=2022-08-07T03:04:48.207650Z, reading=9]

我在我的包层次结构之外的src/main/java添加了一个module-info.java文件。
module LoomEx {
    requires jdk.incubator.concurrent;
}

我的POM。我从列表中的Apache Maven QuickStart原型开始。然后我将所有版本号更新为最新的。

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns = "http://maven.apache.org/POM/4.0.0"
         xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>work.basil.example</groupId>
    <artifactId>Loom</artifactId>
    <version>1.0-SNAPSHOT</version>

    <name>Loom</name>
    <!-- FIXME change it to the project's website -->
    <url>http://www.example.com</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.release>19</maven.compiler.release>
    </properties>

    <dependencies>

        <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.9.0</version>
            <scope>test</scope>
        </dependency>


    </dependencies>

    <build>
        <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (maybe moved to parent pom) -->
            <plugins>
                <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
                <plugin>
                    <artifactId>maven-clean-plugin</artifactId>
                    <version>3.2.0</version>
                </plugin>
                <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
                <plugin>
                    <artifactId>maven-resources-plugin</artifactId>
                    <version>3.3.0</version>
                </plugin>
                <plugin>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.10.1</version>
                    <configuration>
                        <!--<compilerVersion>19</compilerVersion>-->
                        <release>19</release>
                        <!--<compilerArgs>&#45;&#45;source 19</compilerArgs>-->
                        <compilerArgs>--enable-preview</compilerArgs>
                        <!--<compilerArgs>&#45;&#45;add-modules jdk.incubator.concurrent</compilerArgs>-->
                    </configuration>
                </plugin>
                <plugin>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>3.0.0-M7</version>
                </plugin>
                <plugin>
                    <artifactId>maven-jar-plugin</artifactId>
                    <version>3.2.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-install-plugin</artifactId>
                    <version>3.0.1</version>
                </plugin>
                <plugin>
                    <artifactId>maven-deploy-plugin</artifactId>
                    <version>3.0.0</version>
                </plugin>
                <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
                <plugin>
                    <artifactId>maven-site-plugin</artifactId>
                    <version>4.0.0-M3</version>
                </plugin>
                <plugin>
                    <artifactId>maven-project-info-reports-plugin</artifactId>
                    <version>3.4.0</version>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</project>

我尝试使用从thisthis以及this等页面中获取的各种Maven POM元素,以及JEP 11:孵化器模块JEP 428: 结构化并发(孵化器)的提示,希望不需要module-info.java文件。但是我无法使其正常工作。对我来说,添加模块信息文件是成功的唯一途径。

当然,我还必须设置通常隐藏在各种不同位置的IntelliJ多个奇怪设置,以指定使用Java 19。请参阅其他Stack Overflow问题,了解有关这些麻烦设置的说明。(让我发疯了。绝对是IntelliJ中最烦人的问题/缺陷。为什么IntelliJ不能从Maven POM或Gradle设置中读取Java版本并完成它呢?)


谢谢,但是添加一个module-info.java文件会破坏我的构建方式。 - Eric Kolotyluk
@EricKolotyluk 我明白。我也想能够在没有 module-info.java 文件的情况下构建。但是我还没有找到方法。 - Basil Bourque
1
你的示例代码在没有导入jdk.incubator.concurrent.StructuredTaskScope库的情况下运行正常吗? - Holger

4

我成功地使用了Maven,而不需要module-info.java文件。 我的POM和您的POM之间的主要区别似乎在于我如何设置编译器参数。 而不是你的:

<configuration>
  <compilerArgs>--source 19</compilerArgs>
  <compilerArgs>--enable-preview</compilerArgs>
  <compilerArgs>--add-modules jdk.incubator.concurrent</compilerArgs>
  <compilerVersion>19</compilerVersion>
  <source>19</source>
  <target>19</target>
</configuration>

我拥有:

<configuration>
  <compilerArgs>
    <arg>--add-modules=jdk.incubator.concurrent</arg>
  </compilerArgs>
</configuration>

注意我在中嵌套了元素。虽然我也在--add-modules参数中使用了等号=,但我不知道那是否是必须的。看来--enable-preview只需要在运行时而不是编译时使用。你拥有的其他参数我以另一种方式进行设置(例如,元素)。

示例

以下是一个示例,其中设置为JDK 19安装。

源代码/构建文件

App.java:

package sample;

import jdk.incubator.concurrent.StructuredTaskScope;

public class App {
  public static void main( String[] args ) throws Exception {
    try (var scope = new StructuredTaskScope<Void>()) {
      scope.fork(() -> delayPrint(1000, "Hello,"));
      scope.fork(() -> delayPrint(2000, "World!"));
      scope.join();
    }
    System.out.println("Done!");
  }

  private static Void delayPrint(long delay, String message) throws Exception {
    Thread.sleep(delay);
    System.out.println(message);
    return null;
  }
}

pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId>
  <artifactId>sample</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>sample</name>
  <url>http://maven.apache.org</url>
  
  <properties>
    <maven.compiler.source>19</maven.compiler.source>
    <maven.compiler.target>19</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <build>

    <plugins>

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.10.1</version>
        <configuration>
          <compilerArgs>
            <arg>--add-modules=jdk.incubator.concurrent</arg>
          </compilerArgs>
        </configuration>
      </plugin>

      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <version>3.1.0</version>
        <configuration>
          <executable>${java.home}/bin/java</executable>
          <arguments>
            <argument>--add-modules=jdk.incubator.concurrent</argument>
            <argument>--enable-preview</argument>
            <argument>--class-path</argument>
            <classpath/>
            <argument>sample.App</argument>
          </arguments>
        </configuration>
      </plugin>

    </plugins>
  </build>

</project>

输出

编译:

> mvn compile

[INFO] Scanning for projects...
[INFO]
[INFO] -------------------------< com.example:sample >-------------------------
[INFO] Building sample 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ sample ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory C:\Users\***\Desktop\structured_concurrency\sample\src\main\resources
[INFO]
[INFO] --- maven-compiler-plugin:3.10.1:compile (default-compile) @ sample ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to C:\Users\***\Desktop\structured_concurrency\sample\target\classes
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.439 s
[INFO] Finished at: 2022-08-08T01:01:26-06:00
[INFO] ------------------------------------------------------------------------

执行:

> mvn exec:exec

[INFO] Scanning for projects...
[INFO]
[INFO] -------------------------< com.example:sample >-------------------------
[INFO] Building sample 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- exec-maven-plugin:3.1.0:exec (default-cli) @ sample ---
WARNING: Using incubator modules: jdk.incubator.concurrent
Hello,
World!
Done!
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  2.610 s
[INFO] Finished at: 2022-08-08T01:02:42-06:00
[INFO] ------------------------------------------------------------------------

谢谢,但我无法让你的compilerArgs工作。我总是从“mvn compile”得到“错误:包jdk.incubator.concurrent不可见”。 - Eric Kolotyluk
我将其复制粘贴到了一个新项目中。奇怪的是,该项目可以编译和构建。但是在运行“main”时,我会收到以下异常:Caused by: java.lang.ClassNotFoundException: jdk.incubator.concurrent.StructuredTaskScope - Basil Bourque
@BasilBourque 这很奇怪。--add-modules 应该是有效的,否则我相信你会得到一个 FindException。但是能够解析模块,然后在该模块中找不到类?我不确定那里发生了什么。我刚刚尝试将其复制并粘贴到一个新项目中,在我的计算机上(Windows 10)它仍然完美地工作。 - Slaw
@EricKolotyluk 不确定那是什么意思。你的 JAVA_HOME 是否指向 JDK 19 安装路径?如果有关系的话,我使用的 Java 版本是 19-ea+34-2229。如果你使用的是旧版本,也许自那时以来已经修复了一些问题。我也在使用 Windows 10,虽然我相信我所做的事情是跨平台的。 - Slaw
1
我也可以在Ubuntu WSL上使其工作。 在WSL和Windows两种情况下,我都使用的是Maven 3.8.6和OpenJDK 19-ea+34-2229。 - Slaw
我无法对此答案进行改进,但是我有一个可工作的示例,如果对任何人有用的话 - https://github.com/codetojoy/gists_java/tree/main/egg_StackOverflow_73229247。请注意,代码非常不同,但它说明了Maven构建。此外,还有一个Github Action。(请注意,示例是较大项目中的一个文件夹)。 - Michael Easter

2

对于@BasilBourque 精彩回答的小补充。 对于那些可能不使用Maven或模块的人,您无需将应用程序变为模块。 在所有启动器中都需要--enable-preview --add-modules jdk.incubator.concurrent参数,无论是直接还是通过Maven。

您应该能够使用标准类路径和没有module-info.java的以下源代码启动器或javac / java从命令行运行他提供的并发示例:

%JAVAHOME%/bin/java --source 19 --enable-preview --add-modules jdk.incubator.concurrent ConcurrencyEx.java

或者编译后,执行以下命令:

%JAVAHOME%/bin/javac --source 19 --enable-preview --add-modules jdk.incubator.concurrent -d ../build ConcurrencyEx.java

%JAVAHOME%/bin/java --enable-preview --add-modules jdk.incubator.concurrent -cp ../build ConcurrencyEx

显然,只需将%JAVAHOME%替换为$JAVAHOME即可在Linux上运行,而不是Windows。

源代码:

import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadLocalRandom;
import jdk.incubator.concurrent.*;

public class ConcurrencyEx {
    
    // main as from @BasilBourque answer
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        record Event( UUID id , Instant when , Integer reading ) {}

        try ( var scope = new StructuredTaskScope.ShutdownOnFailure() ) {
            Future < UUID > futureId = scope.fork( ( ) -> UUID.randomUUID() );
            Future < Instant > futureWhen = scope.fork( ( ) -> Instant.now() );
            Future < Integer > futureReading = scope.fork( ( ) -> ThreadLocalRandom.current().nextInt( 1 , 10 ) );

            scope.join();           // Join both forks
            scope.throwIfFailed();  // ... and propagate errors
            Event event = new Event( futureId.get() , futureWhen.get() , futureReading.get() );
            System.out.println( event );
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接