正确做法是用matrix策略驱动job实例化:GitHub Actions通过jobs.test.strategy.matrix.java-version配置多版本,GitLab CI用parallel配合JAVA_VERSION变量,并显式指定Temurin等发行版以避免license问题。

CI中并行跑多个Java版本要靠matrix策略
GitHub Actions和GitLab CI都原生支持矩阵式执行,不用写重复job。关键不是“怎么装多个JDK”,而是“怎么让同一套测试逻辑在不同JDK下自动触发”。硬编码多个script块或手动拉镜像只会让配置膨胀、难维护。
常见错误是把多个setup-java塞进同一个job里——它只生效最后一次,前面的被覆盖。正确做法是用strategy: matrix驱动整个job实例化。
- GitHub Actions:在
jobs.test.strategy.matrix.java-version里列['8', '17.0.7', '21.0.3', '25-ea'],每个值生成一个独立runner - GitLab CI:用
parallel: 4配合variables: { JAVA_VERSION: ["8", "17", "21", "25"] },再在script里用source /etc/profile.d/java-${CI_VARIABLES_JAVA_VERSION}.sh加载对应环境 - 必须显式指定发行版,比如
distribution: 'temurin',避免Oracle OpenJDK在CI中因license变更突然不可用
验证javac和java版本一致性不能只看java -version
CI里最常漏检的是编译器和运行时版本错位。比如Maven用了javac 17编译,但java -version显示11,测试会静默失败——尤其涉及record、sealed或--enable-preview特性时。
建议在每个job开头加校验脚本:
java -version && javac -version && \ [ "$(java -version | head -1 | cut -d' ' -f2)" = "$(javac -version 2>&1 | cut -d' ' -f2)" ] || exit 1
- ARM和x86 runner路径不同:
/usr/lib/jvm/java-17-openjdk-amd64vs/usr/lib/jvm/java-17-openjdk-arm64,JAVA_HOME必须动态推导,别硬写 - Java 8不支持
javac -version输出格式统一,要用javac -help 2>&1 | head -1兼容 - Java 25早期访问版(EA)的
java -version含25-ea字样,正则匹配要预留-ea分支
Docker镜像里多版本共存得靠软链+profile.d隔离
想在一个CI容器里切JDK?别用update-alternatives——它依赖deb/rpm包管理,在slim镜像里通常没装。更稳的方式是预装多个JDK到固定路径,用软链指向当前激活版本,并通过/etc/profile.d/里的shell脚本按需重置JAVA_HOME和PATH。
- 例如:把
jdk8解压到/opt/jdks/jdk8u392-b08,jdk17到/opt/jdks/jdk-17.0.7+7,再建/opt/jdk软链指向任一目标 - 在
/etc/profile.d/java.sh里写:export JAVA_HOME=/opt/jdk; export PATH=$JAVA_HOME/bin:$PATH - CI脚本中切换只需:
rm /opt/jdk && ln -s /opt/jdks/jdk8u392-b08 /opt/jdk && source /etc/profile.d/java.sh - 注意:Docker层缓存会让
/opt/jdk软链在build阶段固化,runtime切换必须在script:里执行,不能写进Dockerfile
旧版本JDK 8在现代CI runner上容易因glibc或TLS报错
JDK 8u392之前版本依赖较老的glibc符号(如GLIBC_2.14),而Ubuntu 24.04+或GitHub-hosted runners已升级到glibc 2.39,直接运行会报symbol not found。这不是Java配置问题,是二进制兼容性断层。
- 方案一:降级runner镜像,比如GitHub Actions用
ubuntu-20.04(glibc 2.31),而非默认ubuntu-latest - 方案二:换用Eclipse Temurin JDK 8u392+,它已适配新glibc,下载地址:
https://github.com/adoptium/temurin8-binaries/releases/download/jdk8u392-b08/OpenJDK8U-jdk_x64_linux_hotspot_8u392b08.tar.gz - 方案三:彻底弃用JDK 8做CI验证,改用
javac --release 8编译+JDK 17运行时测试——能覆盖95%的兼容性场景,且无需真机JDK 8
真正麻烦的不是装几个JDK,而是旧版本在新内核、新libc、新TLS栈上的隐式失效。验证逻辑越靠近真实运行环境(比如用Docker模拟生产OS),越早暴露这类底层不兼容。











