一、Java11的新特性

新特性列举

局部变量类型推断:Java 11支持在局部变量的声明中使用var关键字进行类型推断。通过省略变量的类型,可以更简洁地声明局部变量,提高代码的可读性。

字符串API增强:Java 11引入了一些新的方法和功能来处理字符串。其中包括isBlank()方法用于检测字符串是否为空白,lines()方法用于将字符串拆分成行,以及strip()、stripLeading()和stripTrailing()等方法用于去除字符串前后的空格。

HTTP客户端标准化:Java 11中引入了一个标准的HTTP客户端API(java.net.http),用于发送HTTP请求和处理响应。这个新的HTTP客户端提供了异步和同步请求的支持,和流式处理,以及更容易使用的API。

垃圾回收器改进:Java 11引入了一个新的垃圾回收器(Epsilon GC),它是一种无操作(No-Op)的垃圾回收器,用于在测试和性能调优场景下进行使用。此外,Java 11还改进了现有的垃圾回收器,如G1垃圾回收器和ZGC。

动态类文件常量:Java 11引入了一种新的类型常量——动态类文件常量(Dynamic Class File Constants)。它可以在运行时通过invokedynamic指令来使用,并且可以在不重新编译代码的情况下更新。

新的标准化HTTP/2客户端和服务器API:在Java 9中引入的标准化HTTP/2 API得到了改进和增强。HTTP/2通过复用连接、头部压缩和并行请求等技术提升了网络性能。Java 11提供了更完善的HTTP/2客户端和服务器API,使得开发者可以更方便地使用和构建基于HTTP/2协议的应用程序。

Flight Recorder事件流处理:Java 11将Flight Recorder的事件流数据开放给开发者,以便进行实时处理和分析。这使得开发者可以更加详细地监视和调试应用程序的性能、行为和异常情况。

var 关键字:var 是Java 10中引入的关键字,Java 11又扩展了它的使用范围。现在可以在Lambda表达式的参数列表中使用 var 来声明变量类型,并且可以在for循环中使用 var 来遍历数组或集合。

集合 API 增强:Java 11中的集合API有一些新增的方法和功能,例如 List.of()、Set.of() 和 Map.of() 可以快速创建不可变的集合。另外,新增了 Collection.toArray(IntFunction<T[]> generator) 方法,用于生成指定类型的数组。

ZGC 垃圾回收器:Java 11中引入了全新的ZGC垃圾回收器,它是一个低停顿的、可扩展的、并行的垃圾回收器,可以处理TB级别的堆内存。相比其他垃圾回收器,ZGC 能够更好地平衡吞吐量、延迟和内存占用。ZGC是一种可伸缩、低延迟的垃圾收集器,专为大内存和高吞吐量应用程序设计。它通过并发收集和部分压缩技术实现了可控制的垃圾回收停顿时间,并具有较低的延迟和高处理能力。

Epsilon 垃圾回收器:Epsilon 是Java 11中引入的一种新的垃圾回收器,它不执行实际的垃圾回收操作,仅仅负责对内存进行分配和释放。因此,Epsilon 可以用于那些内存使用频繁,但是不需要垃圾回收的场景。

要启用Epsilon垃圾收集,请在启动Java应用程序时添加JVM参数:

-XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC

Unicode 10 支持:Java 11对Unicode进行了更新,支持Unicode版本10。Unicode 10增加了数百个字符和符号,包括新的表情符号和文字。Java 11的更新确保了对最新Unicode标准的支持,使得开发者可以处理更广泛的字符和文本编码。

新版Javadoc:Java 11中改进了Javadoc工具,使其更加易读和易用。新版Javadoc增强了搜索和导航功能,并且支持HTML5和CSS3等最新的Web技术。

Flight Recorder API:Java 11将Java Flight Recorder(JFR)引入了OpenJDK,作为其标准特性的一部分。JFR是一种轻量级事件记录器,可用于实时监测和分析JVM的性能和行为。它提供了丰富的事件数据,包括垃圾收集、线程活动、方法调用等,并能够以极低的开销连续记录应用程序运行时的信息。

嵌套访问控制:Java 11中引入了嵌套访问控制,可以让内部类和外部类之间共享私有成员。如果一个类是另一个类的嵌套类,并且它们在同一个源代码文件中,那么它们之间可以互相访问私有成员。

支持动态分配 Compiler Threads:JVM启动参数新增-XX:+UseDynamicNumberOfCompilerThreads,动态的控制编程线程的数量,原来的编译线程默认会启动大量造成cpu和memory浪费。

移除的功能:Java 11中还移除了一些过时或不再推荐使用的功能,如Applet API、JavaFX、Nashorn JavaScript引擎等。废弃了一些API,主要是Java EE 和 CORBA 相关的API,包括 java.corba、java.transaction、javax.enterprise 和 javax.jms 等。

Nashorn曾经是Java平台上的一种JVM内置的JavaScript引擎,但由于缺乏维护和性能优势不明显等原因,Java 11决定弃用该引擎,推荐开发者使用外部的JavaScript引擎


代码示例

 嵌套于访问控制的上下文(Nest-Based Access Control)

Java 11通过JEP 181引入了嵌套类之间更简洁的访问控制。这个特性通过引入新的字节码指令,使得嵌套类之间的访问更加高效。这意味着在Java 11中,嵌套类可以直接访问彼此的私有成员,而无需使用桥接方法。

代码如下

public class OuterClass {
    private int outerValue;

    public class InnerClass {
        private int innerValue;

        public int accessOuterValue() {
            return outerValue; // 直接访问外部类的私有成员
        }
    }

    public int accessInnerValue(InnerClass inner) {
        return inner.innerValue; // 直接访问内部类的私有成员
    }
}

动态类文件常量(Dynamic Class- Constants)

Java 11通过JEP 309为常量池带来了一种新形式:动态常量。动常量允许在运行时析常量值,这使得类文件可以表示更丰富的常量表达式,同时保持紧凑和高效。

动态常量主要用于底层字节码操作和库开发。以下是一个使用invokedynamic指令创建动态量的示例:

import java.lang.invoke.*;

public class DynamicConstants {
    public static void main(String[] args) throws Throwable {
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodType mt = MethodType.methodType(String.class, int.class);
        CallSite cs = (CallSite) lookup.findStatic(DynamicConstants.class, "bootstrap", mt).invoke(0);
        String result = (String) cs.getTarget().invoke(42);
        System.out.println(result);
    }

    public static CallSite bootstrap(MethodHandles.Lookup lookup, int value) {
        MethodHandle mh = lookup.findStatic(String.class, "valueOf", MethodType.methodType(String.class, int.class));
        return new ConstantCallSite(mh);
    }
}

动态常量主要用于高级开发场景,如库开发和字节码操作。在日常应用开发中,您可能不会直接使用这个特性

新的Http客户端API

Java 11引入了JEP 321,为开发者提供了一个新的HTTP客户端API,用于支持HTTP/1.1HTTP/2和WebSocket通信。这个API是在Java 9中引入的incubator模的基础上改进和标准化的。

// 异步请求
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder(URI.create("http://goodysr.cn/")).build();
HttpResponse.BodyHandler<String> stringBodyHandler = HttpResponse.BodyHandlers.ofString();
CompletableFuture<HttpResponse<String>> future = client.sendAsync(request, stringBodyHandler);
HttpResponse<String> response =  future.get();
// 返回结果
String body = response.body();


// 同步请求
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder(URI.create("http://goodysr.cn/")).build();
HttpResponse.BodyHandler<String> stringBodyHandler = HttpResponse.BodyHandlers.ofString();
HttpResponse<String>  send = client.send(request, stringBodyHandler);
String body = send.body();

下面是GET请求 异步处理响应的实例代码

HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://api.example.com/data"))
        .build();

client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
        .thenApply(HttpResponse::body)
        .thenAccept(System.out::println);

局部变量语法的改进

Java 11通过JEP 323改进了局部变量类型推断(var),使其可以在lambda表达式的参数中使用。

以下是一个使用var在lambda表达中推断参数类型的示例:

import java.util.List;
import java.util.stream.Collectors;

public class VarInLambda {
    public static void main(String[] args) {
        List<String> names = List.of("Alice", "Bob", "Charlie");
        List<Integer> nameLengths = names.stream()
                .map((var name) -> name.length())
                .collect(Collectors.toList());
        System.out.println(nameLengths);
    }
}
// 在Java 11之前
List<String> names = new ArrayList<>();

// Java11 使用局部变量类型推断
var names = new ArrayList<String>();

// 在Java 11之前,无法省略未使用的参数
Consumer<String> consumer = (String x) -> System.out.println("Hello, " + x);

// 使用扩展Lambda参数名称
Consumer<String> consumer = (_) -> System.out.println("Hello, World!");

// 在Java 11之前
Function<Integer, String> function = (Integer x) -> String.valueOf(x);

// 使用Lambda参数类型推断
Function<Integer, String> function = (x) -> String.valueOf(x);


List<String> names = List.of("Alice", "Bob", "Charlie");

// Java 11之前需要使用collect(Collectors.toList())
List<String> upperCaseNames = names.stream()
                                   .map(String::toUpperCase)
                                   .collect(Collectors.toList());

// Java 11中使用toList()方法
List<String> upperCaseNames = names.stream()
                                   .map(String::toUpperCase)
                                   .toList();
  • 使用var可以提高代码的简洁性,但请确保在适当的场景中使用它,以保持代码的可读性。

依赖分析

Java 11引入了JEP 335,它提供了一种新的工具,用于分析模块之间的依赖关系。这有助于开发者更好地理解和管理模块化Java应用程序的依赖

要使用依赖分析工具,请在命令行中输入jdeps,后跟您要分析的块或类文件的路径。

jdeps -summary my-module.jar

字符串API增强

String text = "   Hello, World!   ";

// 检测字符串是否为空白
if (text.isBlank()) {
    System.out.println("The string is blank.");
}

// 删除字符串前后的空格
String trimmedText = text.strip();
System.out.println(trimmedText);  // 输出: "Hello, World!"

// 将字符串拆分成行
text.lines().forEach(System.out::println);

// 判断字符串字符是否都是空
String str = " abc ";
System.out.println(str.isBlank());
// 去除字符串首尾空白
System.out.println(str.strip());
System.out.println(str.trim());
// 去除首部的字符串空格
System.out.println(str.stripLeading());
// 去除尾部的字符串空格
System.out.println(str.stripTrailing());
// 复制字符串
str2 = str2.repeat(3);
System.out.println(str2); // abc abc abc
// 行数统计
long count = str2.lines().count();
System.out.println(count); // 1行

File API增强

// 创建临时文件
Path path = Files.writeString(Files.createTempFile("test", ".txt"), "http://www.csdn.net");
System.out.println(path);
 
Files.writeString(Path.of("./", "tmp.txt"), "hello, jdk11 files api",StandardCharsets.UTF_8); 
String s = Files.readString(Paths.get("./tmp.txt"), StandardCharsets.UTF_8);

集合 API增强

List<String> list = List.of("Java", "Python", "Ruby");


// 旧的方法:传入String[]:
String[] oldway = list.toArray(new String[list.size()]);
 
// 新的方法:传入IntFunction:
String[] newway = list.toArray(String[]::new);

Stream API增强

List<Integer> lsit1 = Arrays.asList(1, 3, 4, 6, 2);
// takeWhile 从开头开始获取满足的元素,直到不满足为止
List<Integer> c1 = lsit1.stream().takeWhile(x -> x < 3).collect(Collectors.toList());
System.out.println(c1); // 结果是 3


// dropWhile 与takeWhile相反,取出不满足的,直到满足为止
List<Integer> c2 = lsit1.stream().dropWhile(x -> x < 3).collect(Collectors.toList());
System.out.println(c2); // 结果是 3


// ofNullable
Stream<Object> o1 = Stream.ofNullable(null);
long count = o1.count(); // 结果是0  不会出现异常
System.out.println(count);

Optional增强

// ofNullable 支持存空 如果值为null,取默认值,不会报空指针
String name = (String) Optional.ofNullable(null).orElse("默认值");
// orElseGet 与 orElse的区别,orElseGet可以定义逻辑补充
String name2 = (String) Optional.ofNullable(null).orElseGet(() -> "默认值 - 可以有一些操作");
System.out.println(name2);
// 如果值为空,会报错异常 - 指定异常 (没有不会出现异常)
try {
    String name3 = (String)  Optional.ofNullable(null).orElseThrow(() -> new Exception("指定异常"));
} catch (Throwable throwable) {
    throwable.printStackTrace();
}


Java11升级后的效果

JDK11相对于JDK8,所有垃圾回收器的性能都有提升,特别是大内存机器下G1的提升最明显

8G内存以下的机器,推荐使用Parallel GC,如果特别追求低延迟,选择牺牲吞吐量,可以使用G1,并设置期望的最大垃圾回收停顿时间来控制

8G及以上的大内存机器,推荐使用G1

不推荐使用CMS,升级后从各项数据来看,CMS收集器都不如G1

二、Java17的新特性

  1. 基于嵌套的访问控制:Java 17引入了基于嵌套的访问控制,允许在类和接口中定义私有的嵌套类型,这些类型只有它们的外围类或接口可以访问。这提供了更精细的访问控制,有助于保护内部实现细节。
  2. 无条件的JVM警告抑制:Java 17新增了@SuppressWarnings("all")注解,可以抑制JVM产生的所有警告。这对于在特定场景下进行代码调试和调优非常有用,可以帮助开发者专注于核心逻辑,忽略不必要的警告信息。
  3. 基于内存的Java(Project Panama):Java 17引入了基于内存的Java,提供了一种新的机制来处理与本地内存交互的方式。这使得Java开发人员能够更高效地操作本地内存,从而提高性能和可伸缩性。
  4. Unix域套接字通信API:Java 17提供了Unix域套接字通信API,用于在本地进程之间进行通信。这对于开发需要本地进程间通信的应用程序非常有用,例如实现高性能的进程间通信(IPC)。
  5. Sealed Class:这是一个受限制的类,用于控制类的继承关系,防止其被错误地扩展或子类化。通过使用sealed关键字,可以指定哪些类可以继承或实现该类,从而提供更加严格的类型安全和代码封闭性。
  6. Pattern Matching for instanceof:Java 17增强了instanceof操作符的模式匹配功能,使编写类型安全的代码更加容易,提高了代码的可读性和可维护性。
  7. Switch表达式的增强:Java 17对Switch表达式进行了改进,支持Lambda表达式和块语句,提供了更灵活和强大的模式匹配能力。
  8. 新的类型推断机制:Java 17改进了类型推断机制,允许在Lambda表达式和匿名内部类中使用var关键字,简化了类型声明的复杂性。
  9. 性能提升:Java 17引入了新的向量API,用于表示在特定硬件上优化的向量计算,可以显著提高数值计算密集型应用程序的性能。此外,对并行垃圾回收器进行了优化,减少了垃圾回收过程中的停顿时间,提升了应用程序的性能和响应速度。

Sealed类

Sealed类是 Java 17 中引入的一项新特性,它可以限制一个类的子类的数量,从而提高代码的安全性和可维护性。Sealed类可以通过使用 sealed 关键字来定义,然后使用 permits 子句来指定允许继承的子类。

public sealed class Shape permits Circle, Rectangle {
    // Shape 类的定义
}

public final class Circle extends Shape {
    // Circle 类的定义
}

public final class Rectangle extends Shape {
    // Rectangle 类的定义
}

Shape 类被声明为 sealed 类,它允许 Circle 和 Rectangle 两个类继承。这意味着,除了这两个类之外,没有其他类可以继承 Shape 类。


Switch 表达式的增强

Java 17 在 Switch 表达式方面进行了增强,可以支持 Lambda 表达式和块语句。以下是一个简单的示例:

String day = "MONDAY";
int numLetters = switch (day) {
    case "MONDAY", "FRIDAY", "SUNDAY" -> 6;
    case "TUESDAY"                -> 7;
    case "THURSDAY", "SATURDAY"   -> 8;
    case "WEDNESDAY"              -> 9;
    default                      -> 0;
};
System.out.println(numLetters); // 输出 6

default不能使用break了,如果出现期望之外的值,就抛出异常

public class TestSwitch {
    public static void main(String[] args) {
        test1("*", 5, 6);
    }

    private static void test1(String sign, int num1, int num2) {
        int ret = switch (sign) {
            case "+" -> {
                System.out.println("this is add");
                yield num1 + num2;// 下面的是简化写法
            }
            case "-" -> num1 - num2;
            case "*" -> num1 * num2;
            case "/" -> num1 / num2;
            default -> throw new IllegalStateException("Unexpected value: " + sign);
        };
        System.out.println("ret: " + ret);
    }
}

新的类型推断机制

Java 17 改进了类型推断机制,允许在 Lambda 表达式和匿名内部类中使用 var 关键字。以下是一个简单的示例

var list = new ArrayList<String>();
list.add("Java 17");
list.add("新特性");
list.forEach(s -> System.out.println(s));

实例方法引用

List<Integer> list = Arrays.asList(3, 1, 4, 1, 5, 9);
list.stream().distinct().sorted().forEach(System.out::println);

HTTP客户端改进

Java 17 中引入了 HTTP/2 客户端,这个特性可以让开发人员更方便地使用 HTTP/2 协议来进行网络通信。HTTP/2 客户端可以提供更快的网络传输速度和更低的延迟,从而提高应用程序的性能和用户体验。

HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://www.example.com/"))
        .build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());

ZGC的改进

ZGC 是一种基于 Region 的低延迟垃圾收集器,Java 17 引入了以下新特性:

  • 卸载类:在不停机的情况下,卸载不需要的类,可以减少内存占用。
  • 日志记录:提供了更丰富的日志记录选项,方便进行调试和性能分析。
// 启用 ZGC
java -XX:+UnlockExperimentalVMOptions -XX:+UseZGC -jar MyApp.jar
 
// 在代码中启用 ZGC
VMOption option = new VMOption("-XX:+UnlockExperimentalVMOptions");
VMOption option2 = new VMOption("-XX:+UseZGC");
List<VMOption> options = Arrays.asList(option, option2);
Launcher launcher = Launcher.createLauncher(options.toArray(new VMOption[0]));
launcher.launch("com.example.MyApp", "arg1", "arg2");

C++14标准的支持

Java 17 引入了对 C++ 14 语言标准的支持,可以使用 C++ 14 中的新特性和语法

// C++ 14 中的泛型 Lambda 表达式
auto multiply = [](auto x, auto y) { return x * y; };
int result = multiply(5, 6);
System.out.println(result); // 输出 30

Unicode13的支持

Java 17 支持 Unicode 13,包括新增的字符、块和属性

// 支持 Unicode 13 中的新增字符
String emoji = "🙂";
System.out.println(emoji.length()); // 输出 2

针对嵌入式系统的改进

Java 17 改进了对嵌入式系统的支持,包括增强的垃圾收集器、更小的内存占用和更快的启动时间。以下是一个简单的示例

// 使用 Embedded JRE 启动应用程序
Path path = Path.of("myapp.jar");
Path jrePath = Path.of("jre");
ProcessBuilder pb = new ProcessBuilder(
        jrePath.resolve("bin").resolve("java").toString(),
        "--module-path", jrePath.resolve("mods").toString(),
        "--module", "com.example.myapp",
        "--add-modules", "ALL-MODULE-PATH",
        "-jar", path.toString());
Process process = pb.start();

生成的代码的性能改进

Java 17 引入了一些新特性,可以优化生成的代码的性能,特别是在反射和 Lambda 表达式等方面

// 反射
Class<?> clazz = MyClass.class;
Method method = clazz.getMethod("myMethod");
Object obj = clazz.getDeclaredConstructor().newInstance();
method.invoke(obj);
 
// Lambda 表达式
List<String> list = Arrays.asList("Java", "17", "新特性");
list.stream().filter(s ->

record 关键字

提供了一种简洁的方式来定义不可变的数据类。记录类是一种类似于值类型的数据类型,通常用于表示数据传输对象、API 返回类型、函数参数等等。

public record Person(String name, int age) {}

这个记录类定义了一个名为 Person 的不可变数据类,包含两个属性:name 和 age。记录类自动生成了一个构造函数,以及对应属性的访问器和 equals()/hashCode()/toString() 方法。可以看到,相较于传统的类定义,记录类更加简洁。

Pattern Matching for instanceof

Java 17 中引入了 Pattern Matching for instanceof 特性,它可以让开发人员更方便地使用 instanceof 运算符来进行类型匹配。这个特性可以让开发人员使用类似于 switch 语句的语法来进行类型匹配,从而减少代码的复杂性和错误率。

在if (obj instanceof String str) {
    // obj 是一个字符串,可以使用 str 变量来访问它
    System.out.println(str.length());
} else {
    // obj 不是一个字符串
}

Vector API

Java 17 中引入了 Vector API,这个特性可以让开发人员更方便地使用向量操作来进行数据处理。Vector API 可以提供更高的并行性和更好的性能,从而加速数据处理过程。

// 创建一个 Vector
Vector<Float> v = Vector.of(1.0f, 2.0f, 3.0f, 4.0f);

// 对 Vector 中的元素进行操作
Vector<Float> result = v.map(x -> x * 2).add(Vector.of(1.0f, 1.0f, 1.0f, 1.0f));

// 输出结果
System.out.println(result);

文本块的处理

Java处理json或者xml文本时,需要多个繁琐的嵌套转义,代码写着费劲,维护更费劲。从Java13开始引入了文本块(Text blocks),上代码

package cn.dayangshuo;

/**
 * @author DAYANG
 */
public class TextBlocks {
    public static void main(String[] args) {
        //需要转义
        String jsonStr = "{\"name\": \"大阳\"}";
        //文本块,不需要转义字符
        String textBlocks = """
                {"name": "大阳"}
                """;
        System.out.println(jsonStr);
        System.out.println(textBlocks);
    }
}

基于内存操作的API

import java.lang.invoke.VarHandle;

public class AtomicDemo {
    private static final VarHandle COUNT_HANDLE;

    static {
        try {
            COUNT_HANDLE = MethodHandles.lookup()
                    .findVarHandle(AtomicDemo.class, "count", int.class);
        } catch (Exception e) {
            throw new Error(e);
        }
    }

    private volatile int count; // 需要使用 volatile 关键字

    public void increment() {
        int oldValue;
        int newValue;
        do {
            oldValue = (int) COUNT_HANDLE.getVolatile(this);
            newValue = oldValue + 1;
        } while (!COUNT_HANDLE.compareAndSet(this, oldValue, newValue));
    }

    public int getCount() {
        return count;
    }
}

增强型伪随机数生成器

增强型伪随机数生成器为PRNG提供了新的接口类型和实现,包括可跳转的PRNG和一类额外的可拆分PRNG算法(LXM)。增强型伪随机数生成器由JEP 356并在JDK 17中作为正式功能提供。

要使用增强型伪随机数生成器,需要使用java.util.random包中的新的接口和类。例如,我们可以使用RandomGenerator接口来获取一个PRNG的实例,并且使用它来生成各种类型的随机数:

public static void generateRandomNumbers() {
    RandomGenerator random = RandomGenerator.getDefault();
    System.out.println("A random boolean: " + random.nextBoolean());
    System.out.println("A random int: " + random.nextInt());
    System.out.println("A random long: " + random.nextLong());
    System.out.println("A random float: " + random.nextFloat());
    System.out.println("A random double: " + random.nextDouble());
}

ConcurrentHashMap在Java各版本的改进

Java 8之前的ConcurrentHashMap

ConcurrentHashMap的实现原理主要基于分段锁(Segmentation Lock)的机制,这种设计使得它能够在高并发环境下提供良好的性能。

Java新版本特性_Java新版本特性

1、内部结构与初始化

ConcurrentHashMap内部主要由三个组件构成:一个Segment数组、哈希函数和键值对节点。其中,Segment是一个可重入的互斥锁,每个Segment包含一个哈希表,哈希表中的每个元素都是一个链表。

在初始化ConcurrentHashMap时,会创建一个Segment数组,并指定初始容量和负载因子。每个Segment的初始容量和负载因子与整个ConcurrentHashMap的相同。此外,还会为每个Segment分配一个锁,用于控制对该Segment的并发访问。

2、Segment类

Segment类是ConcurrentHashMap实现并发控制的核心。它继承自ReentrantLock,拥有自己的锁,并且包含一个哈希表。Segment类中的哈希表结构与普通的HashMap类似,采用链表解决哈希冲突。每个链表节点包含一个键值对和一个指向下一个节点的引用。

除了哈希表之外,Segment还维护了一些统计信息,如元素数量、修改次数等。这些信息用于支持扩容和迭代器操作。

3、并发控制

当线程需要访问ConcurrentHashMap中的某个键时,它会首先计算键的哈希值,并根据哈希值的高位定位到对应的Segment。然后,线程会尝试获取该Segment的锁。如果锁已经被其他线程持有,则当前线程会等待直到获取锁为止。

一旦线程获得Segment的锁,它就可以在该Segment内部进行哈希表的查找、插入或删除操作。这些操作与普通的HashMap类似,但需要在锁的保护下进行以确保线程安全。完成操作后,线程会释放锁,使得其他线程有机会访问该Segment。

需要注意的是,虽然每个Segment都有自己的锁,但整个ConcurrentHashMap的并发性能并不完全取决于锁的数量。实际上,锁的竞争程度、哈希函数的分布性以及负载因子等因素都会对并发性能产生影响。

4、扩容与重哈希

当某个Segment的负载因子超过阈值时,会触发扩容操作。扩容时,会创建一个新的Segment数组,并将原有Segment中的键值对重新散列到新的Segment数组中。这个过程涉及到大量的数据复制和重哈希计算。

为了减少扩容对并发性能的影响,ConcurrentHashMap采用了分段扩容的策略。它每次只处理一个Segment,并且在扩容过程中仍然允许其他线程访问未处理的Segment。这样确保了扩容操作不会阻塞整个ConcurrentHashMap的并发访问。

此外,在扩容过程中,ConcurrentHashMap还采用了一种称为“转移策略”的技术来避免死锁和饥饿问题。具体来说,当某个线程正在处理一个Segment时,如果该Segment需要扩容,那么扩容操作会由另一个线程来完成。这样确保了处理线程不会因等待扩容而阻塞过长时间。

分段锁的设计逐渐暴露出一些问题,如内存占用较大、扩容操作复杂等。

Java 8中的ConcurrentHashMap

在Java 8中,ConcurrentHashMap的实现原理发生了显著的变化,它摒弃了之前版本中的分段锁(Segmentation Lock)机制,转而采用了一种更为高效和灵活的并发控制策略,即CAS(Compare-and-Swap)操作结合synchronized同步块。这种新的设计不仅简化了数据结构,还提高了在多核处理器环境下的并发性能。

Java 8中的ConcurrentHashMap底层数据结构主要由数组、链表和红黑树组成。数组用于存储键值对的节点,每个节点要么是一个链表,要么是一个红黑树。当链表长度超过一定阈值(默认为8)时,链表会转换为红黑树,以提高搜索效率。

Java新版本特性_Java新版本特性_02

2、并发控制

2.1. CAS操作

CAS(Compare-and-Swap)是一种无锁化的算法,它包含三个操作数——内存位置(V)、预期原值(A)和新值(B)。如果内存位置V的值与预期原值A相匹配,那么处理器会自动将该位置的值更新为新值B。否则,处理器不做任何操作。无论哪种情况,它都会在CAS指令之前返回该位置的值。在ConcurrentHashMap中,CAS操作被广泛应用于节点的添加、删除和更新等场景,以确保并发修改的安全性。

2.2. synchronized同步块

尽管CAS操作能够在很大程度上减少锁的竞争,但在某些情况下,仍然需要更严格的同步机制来保证并发操作的正确性。因此,Java 8中的ConcurrentHashMap在必要时会使用synchronized同步块来保护某些关键代码段,如树化操作、扩容等。与分段锁相比,synchronized同步块具有更低的开销和更高的灵活性。

3、哈希计算与定位

与之前的版本类似,Java 8中的ConcurrentHashMap也使用哈希算法来计算键的哈希值,并根据哈希值来定位数组中的索引位置。不同的是,Java 8中的哈希计算过程更加复杂和精细,以减少哈希冲突和提高空间利用率。此外,当发生哈希冲突时,新的键值对会添加到链表或红黑树的末尾,而不是像之前版本那样使用头插法。

4、扩容与重哈希

当ConcurrentHashMap中的元素数量超过数组的容量阈值时,就会触发扩容操作。在扩容过程中,会创建一个新的数组,并将原有数组中的键值对重新散列到新的数组中。与之前的版本不同,Java 8中的扩容操作不再需要对整个数组进行锁定,而是采用了更细粒度的并发控制策略。具体来说,它将数组划分为多个小段(每个小段包含多个桶),并允许多个线程同时处理不同的小段。这样设计可以减少锁的竞争和提高扩容操作的并发性能。

Java 17中的ConcurrentHashMap

1、数据结构

与Java 8相似,Java 17中的ConcurrentHashMap也使用了数组、链表和红黑树作为底层数据结构。数组用于存储键值对的节点,每个节点在哈希冲突时形成链表,当链表长度超过一定阈值(默认为8)并且数组长度大于64时,链表会转换为红黑树,以提高搜索效率。如果数组长度小于等于64,则不会进行树化,而是采用扩容来减少哈希冲突。

2、并发控制

Java 17中的ConcurrentHashMap仍然采用CAS操作和synchronized同步块来实现并发控制。CAS操作用于无锁化的节点添加、删除和更新等操作,而synchronized同步块则用于保护树化、扩容等需要更严格同步的代码段。

不过,在Java 17中,JDK可能对这些操作进行了进一步的优化,以减少不必要的CAS操作和锁竞争,提高并发性能。例如,通过更精细的粒度控制synchronized同步块的范围,或者使用更高效的锁实现等。

3、哈希计算与定位

Java 17中的ConcurrentHashMap哈希计算过程与Java 8类似,但可能包含了一些针对新硬件环境的优化。哈希值用于定位数组中的索引位置,当发生哈希冲突时,新的键值对会添加到链表或红黑树的末尾。

此外,Java 17中的ConcurrentHashMap可能还引入了一些新的哈希算法或哈希冲突解决策略,以进一步减少哈希冲突和提高空间利用率。

4、扩容与重哈希

当ConcurrentHashMap中的元素数量超过数组的容量阈值时,会触发扩容操作。在Java 17中,扩容操作的基本原理与Java 8相似,即创建一个新的数组,并将原有数组中的键值对重新散列到新的数组中。然而,Java 17可能对扩容过程中的并发控制、数据迁移等方面进行了优化和改进。

例如,通过更细粒度的并发控制策略来减少锁的竞争;使用更高效的数据迁移算法来减少扩容过程中的性能开销;或者引入一些新的技术手段来提高扩容操作的并发性能和可靠性等。

5、其他改进和优化

除了上述基本原理外,Java 17中的ConcurrentHashMap还包含一些其他改进和优化:

更好的内存布局和缓存利用:通过优化数据结构的内存布局和访问模式,提高缓存利用率和减少内存访问开销。

更高效的节点操作:通过优化节点的添加、删除和更新等操作,减少不必要的内存分配和垃圾回收开销。

更灵活的参数配置:提供更多的参数配置选项,以便用户根据具体应用场景进行更精细的性能调优。

更完善的错误处理和异常处理机制:增强错误处理和异常处理能力,提高程序的健壮性和可靠性。

三、Java21的新特性

字符串模板  预览功能

String Templates 提供了一种更简洁、更直观的方式来动态构建字符串。通过使用占位符${},我们可以将变量的值直接嵌入到字符串中,而不需要手动处理。在运行时,Java 编译器会将这些占位符替换为实际的变量值。并且,表达式支持局部变量、静态/非静态字段甚至方法、计算结果等特性。

该特性处理字符串的新方法称为:Template Expressions,即:模版表达式。它是 Java 中的一种新型表达式,不仅可以执行字符串插值,还可以编程,从而帮助开发人员安全高效地组成字符串。此外,模板表达式并不局限于组成字符串——它们可以根据特定领域的规则将结构化文本转化为任何类型的对象。

该特性主要目的是为了提高在处理包含多个变量和复杂格式化要求的字符串时的可读性和编写效率。

它的设计目标是:

通过简单的方式表达混合变量的字符串,简化 Java 程序的编写。

提高混合文本和表达式的可读性,无论文本是在单行源代码中(如字符串字面量)还是跨越多行源代码(如文本块)。

通过支持对模板及其嵌入式表达式的值进行验证和转换,提高根据用户提供的值组成字符串并将其传递给其他系统(如构建数据库查询)的 Java 程序的安全性。

允许 Java 库定义字符串模板中使用的格式化语法(java.util.Formatter ),从而保持灵活性。

简化接受以非 Java 语言编写的字符串(如 SQL、XML 和 JSON)的 API 的使用。

支持创建由字面文本和嵌入式表达式计算得出的非字符串值,而无需通过中间字符串表示。

Java 目前支持三种模板处理器:

STR:自动执行字符串插值,即将模板中的每个嵌入式表达式替换为其值(转换为字符串)。

FMT:和 STR 类似,但是它还可以接受格式说明符,这些格式说明符出现在嵌入式表达式的左边,用来控制输出的样式

RAW:不会像 STR 和 FMT 模板处理器那样自动处理字符串模板,而是返回一个 StringTemplate 对象,这个对象包含了模板中的文本和表达式的信息

除了 JDK 自带的三种模板处理器外,你还可以实现 StringTemplate.Processor 接口来创建自己的模板处理器。

STR模板处理器

@Test
    public void STRTest() {
        String sk = "Java 新特性";
        String str1 = STR."\{sk},就是牛";
        System.out.println(str1);
    }
// 结果.....
Java 新特性,就是牛

    @Test
    public void STRTest() {
        int x = 1,y =2;
        String str = STR."\{x} + \{y} = \{x + y}";
        System.out.println(str);
    }
    
 // 也可以调用自定义方法,如下
    @Test
    public void STRTest() {
        String str = STR."\{getSkStr()},就是牛";
        System.out.println(str);
    }

    public String getSkStr() {
        return "Java 新特性";
    }


// 也可以访问成员变量
public record User(String name,Integer age) {
  
}

@Test
public void STRTest() {
  User user = new User("大明哥",18);
  String str = STR."\{user.name()}今年\{user.age()}";
  System.out.println(str);
}


// 可以嵌入表达式
    @Test
    public void STRTest() {
        int i = 0;
        String str = STR."\{i++},\{i++},\{i++},\{i++},\{i++}";
        System.out.println(str);
    }
// 结果......
0,1,2,3,4

FMT模板处理器

@Test
    public void STRTest() {
        SwitchHistory[] switchHistories = new SwitchHistory[]{
                new SwitchHistory("Java 17","第一次预览","JEP 406","引入模式匹配的 Swith 表达式作为预览特性。"),
                new SwitchHistory("Java 18","第二次预览","JEP 420","对其做了改进和细微调整"),
                new SwitchHistory("Java 19","第三次预览","JEP 427","进一步优化模式匹配的 Swith 表达式"),
                new SwitchHistory("Java 20","第四次预览","JEP 433",""),
                new SwitchHistory("Java 21","正式特性","JEP 441","成为正式特性"),
        };

        String history = FMT."""
                Java 版本     更新类型        JEP             更新内容
                %-10s\{switchHistories[0].javaVersion()}  %-9s\{switchHistories[0].updateType()} %-10s\{switchHistories[0].jep()} %-20s\{switchHistories[0].content()}
                %-10s\{switchHistories[1].javaVersion()}  %-9s\{switchHistories[1].updateType()} %-10s\{switchHistories[1].jep()} %-20s\{switchHistories[1].content()}
                %-10s\{switchHistories[2].javaVersion()}  %-9s\{switchHistories[2].updateType()} %-10s\{switchHistories[2].jep()} %-20s\{switchHistories[2].content()}
                %-10s\{switchHistories[3].javaVersion()}  %-9s\{switchHistories[3].updateType()} %-10s\{switchHistories[3].jep()} %-20s\{switchHistories[3].content()}
                %-10s\{switchHistories[4].javaVersion()}  %-9s\{switchHistories[4].updateType()} %-10s\{switchHistories[4].jep()} %-20s\{switchHistories[4].content()}
                """;
        System.out.println(history);
    }

输出如下:

Java 版本     更新类型        JEP             更新内容
Java 17     第一次预览     JEP 406    引入模式匹配的 Swith 表达式作为预览特性。
Java 18     第二次预览     JEP 420    对其做了改进和细微调整         
Java 19     第三次预览     JEP 427    进一步优化模式匹配的 Swith 表达式
Java 20     第四次预览     JEP 433                        
Java 21     正式特性       JEP 441    成为正式特性

有序集合

Sequenced Collections(序列化集合,也叫有序集合),这是一种具有确定出现顺序(encounter order)的集合(无论我们遍历这样的集合多少次,元素的出现顺序始终是固定的)。

SequencedCollection 接口继承了 Collection接口, 提供了在集合两端访问、添加或删除元素以及获取集合的反向视图的方法

interface SequencedCollection<E> extends Collection<E> {
  // New Method
  SequencedCollection<E> reversed();

  // Promoted methods from Deque<E>

  void addFirst(E);
  void addLast(E);

  E getFirst();
  E getLast();

  E removeFirst();
  E removeLast();
}

看一段示例代码

ArrayList<Integer> arrayList = new ArrayList<>();

arrayList.add(1);   // List contains: [1]

arrayList.addFirst(0);  // List contains: [0, 1]
arrayList.addLast(2);   // List contains: [0, 1, 2]

Integer firstElement = arrayList.getFirst();  // 0
Integer lastElement = arrayList.getLast();  // 2

List<Integer> reversed = arrayList.reversed();
System.out.println(reversed); // Prints [2, 1, 0]

以下是操作LinkedHashSet

LinkedHashSet<Integer> linkedHashSet = new LinkedHashSet<>(List.of(1, 2, 3));

Integer firstElement = linkedHashSet.getFirst();   // 1
Integer lastElement = linkedHashSet.getLast();    // 3

linkedHashSet.addFirst(0);  //List contains: [0, 1, 2, 3]
linkedHashSet.addLast(4);   //List contains: [0, 1, 2, 3, 4]

System.out.println(linkedHashSet.reversed());   //Prints [5, 3, 2, 1, 0]

以下是操作LinkedHashMap

LinkedHashMap<Integer, String> map = new LinkedHashMap<>();

map.put(1, "One");
map.put(2, "Two");
map.put(3, "Three");

map.firstEntry();   //1=One
map.lastEntry();    //3=Three

System.out.println(map);  //{1=One, 2=Two, 3=Three}

Map.Entry<Integer, String> first = map.pollFirstEntry();   //1=One
Map.Entry<Integer, String> last = map.pollLastEntry();    //3=Three

System.out.println(map);  //{2=Two}

map.putFirst(1, "One");     //{1=One, 2=Two}
map.putLast(3, "Three");    //{1=One, 2=Two, 3=Three}

System.out.println(map);  //{1=One, 2=Two, 3=Three}
System.out.println(map.reversed());   //{3=Three, 2=Two, 1=One}

分代ZGC

// 启用分代ZGC
java -XX:+UseZGC -XX:+ZGenerational ...

分代 ZGC 可以显著减少垃圾回收过程中的停顿时间,并提高应用程序的响应性能。这对于大型 Java 应用程序和高并发场景下的性能优化非常有价值。

Java新版本特性_Java新版本特性_03

Switch模式匹配

static String formatterPatternSwitch(Object obj) {
    return switch (obj) {
        case Integer i -> String.format("int %d", i);
        case Long l    -> String.format("long %d", l);
        case Double d  -> String.format("double %f", d);
        case String s  -> String.format("String %s", s);
        default        -> obj.toString();
    };
}

Record类增强

Java新版本特性_Java新版本特性_04

package com.xuesong.java.java21.record;

/**
 * JAVA21 新增的使用了记录模式匹配的代码
 * instanceof 增强
 * switch 增强
 */
public class RecordTest {

    // JAVA20 以及以前的方法,需要强转才行
//    static void print(Object data) {
//        if (data instanceof MyRecord) {
//            MyRecord myRecord = (MyRecord) data;
//            System.out.println("name:"+myRecord.name()+",type:"+myRecord.type());
//        }
//    }
    // JAVA21 新增的使用了记录模式匹配的代码
    static void print(Object data) {
        // , instanceof 方法优化,无需强转,下面两种 instanceof 都可以
        if(data instanceof MyRecord1(String name,Integer type)) {
            System.out.println("name:"+name+",type:"+type);
        }
        if (data instanceof MyRecord2 myRecord) {
            System.out.println("name:" + myRecord.name() + ", type:" + myRecord.type());
        }
        // 新增的 switch 匹配增强,两种匹配方式
        switch (data) {
            case MyRecord1 myRecord1 -> System.out.println("MyRecord1 name " + myRecord1.name()
                    + ", type = " + myRecord1.type());
            case MyRecord2 myRecord2 -> System.out.println("MyRecord2 name = " + myRecord2.name()
                    + ", type = " + myRecord2.type());
            default -> throw new IllegalStateException("Unexpected value: " + data);
        }
        switch (data) {
            case MyRecord1(String name, Integer type) -> System.out.println("MyRecord1 name = " + name
                    + ", type = " + type);
            case MyRecord2(String name, Integer type) -> System.out.println("MyRecord2 name = " + name
                    + ", type = " + type);
            default -> throw new IllegalStateException("Unexpected value: " + data);
        }
    }

    public static void main(String[] args) {
        MyRecord1 myRecord1 = new MyRecord1("ZhangSan",1);
        RecordTest.print(myRecord1);
        MyRecord2 myRecord2 = new MyRecord2("Lisi",2);
        RecordTest.print(myRecord2);
    }
}

输出结果为

name:ZhangSan,type:1
MyRecord1 name ZhangSan, type = 1
MyRecord1 name = ZhangSan, type = 1
name:Lisi, type:2
MyRecord2 name = Lisi, type = 2
MyRecord2 name = Lisi, type = 2

Java新版本特性_Java新版本特性_05

虚拟线程

虚拟线程 Java21 最重要的一个更新,优化了原有的 java 线程,原有java线程在大量线程并发的情况下,不停的切换导致效率明显下降,虚拟线程解决了切换的问题,在大量虚拟线程并发的情况下,效率得到极大的提升。

Java新版本特性_Java新版本特性_06

Java新版本特性_Java新版本特性_07

从 Java 代码的角度来看,虚拟线程感觉就像普通线程,但它们没有 1:1 映射到操作系统/平台线程。它是从虚拟线程到载体线程进而到操作系统线程的M:N映射。

有一个所谓的载体线程池,虚拟线程临时映射(“安装”)到该线程池上。一旦虚拟线程遇到阻塞操作,虚拟线程就会从载体线程中移除(“卸载”),并且载体线程可以执行另一个虚拟线程(新的或之前被阻塞的虚拟线程)。

载体线程池是ForkJoinPool

Java新版本特性_Java新版本特性_08


package com.xuesong.java.java21.virtual_thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

/**
 * JAVA21 虚拟线程  以下是普通线程创建和三种虚拟线程的创建方法
 */
public class VirtualThreadTest {
    static int index = 0;
    public static void main(String[] args) {
        // 创建线程
        Runnable runnable = () -> {
            index ++;
            System.out.println("Hello, Xuesong " + Thread.currentThread().getName() + "; index = " + index);
        };
        System.out.println("======virtualThread======");
        // 使用静态构建器方法创建执行
        Thread virtualThread = Thread.startVirtualThread(runnable);
        System.out.println("======Thread.ofVirtual()======");
        Thread.ofVirtual()
                .name("Xuesong virtual thread")
                .start(runnable);
        System.out.println("======Executors.newVirtualThreadPerTaskExecutor()======");
        // 通过Executors的newVirtualThreadPerTaskExecutor方法创建执行虚拟线程
        try (ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor()) {
            for (int i = 0; i < 5; i++) {
                executorService.submit(runnable);
            }
        }
        System.out.println("======virtualThreadFactory======");
        // 通过ThreadFactory创建执行
        ThreadFactory virtualThreadFactory = Thread.ofVirtual()
                .name("Xuesong virtual thread factory", 0)
                .factory();
        Thread factoryThread = virtualThreadFactory.newThread(runnable);
        factoryThread.start();
        factoryThread = virtualThreadFactory.newThread(runnable);
        factoryThread.start();
    }
}

运行输出如下

======virtualThread======
Hello, Xuesong ; index = 1
======Thread.ofVirtual()======
======Executors.newVirtualThreadPerTaskExecutor()======
Hello, Xuesong Xuesong virtual thread; index = 2
Hello, Xuesong ; index = 3
Hello, Xuesong ; index = 6
Hello, Xuesong ; index = 4
Hello, Xuesong ; index = 5
Hello, Xuesong ; index = 7
======virtualThreadFactory======
Hello, Xuesong Xuesong virtual thread factory0; index = 8
Hello, Xuesong Xuesong virtual thread factory1; index = 9

Java新版本特性_Java新版本特性_09