Offizielle Veröffentlichungsankündigung : https://jdk.java.net/21/release-notes
Einführung in Open Source China: https://my.oschina.net/waylau/blog/10112170
Neue Funktionen auf einen Blick:
JEP 431: Sequenzsammlungen
JEP 439: Generations-ZGC
JEP 440: Aufnahmemodus
JEP 441: Switch-Pattern-Matching
JEP 444: Virtuelle Threads
JEP 449: Veraltung des Windows 32-Bit-x86-Ports
JEP 451: Bereiten Sie sich darauf vor, das dynamische Laden von Proxys zu deaktivieren
JEP 452: Schlüsselkapselungsmechanismus-API
JEP 430: String-Vorlagen (Vorschau)
JEP 442: Externe Funktionen und Speicher-API (Dritte Vorschau)
JEP 443: Unbenannte Muster und Variablen (Vorschau)
JEP 445: Unbenannte Klassen- und Instanzhauptmethoden (Vorschau)
JEP 446: Bereichsbezogene Werte (Vorschau)
JEP 453: Strukturierte Parallelität (Vorschau)
JEP 448: Vektor-API (Inkubatorphase 6)
Unter ihnen schenkt jeder dem generationsübergreifenden ZGC und virtuellen Threads mehr Aufmerksamkeit.
Vergleich 17
Der Rahmen wurde von Edelstahl auf Titan aufgerüstet und die Verzeichnisstruktur ist dieselbe:
Die Anzahl der Module beträgt eins weniger als 17:
Die Gesamtgröße wurde von 289 MB auf 320 MB erhöht
herunterladen
POM aktualisieren
Versuchen Sie es mit Laufen
Antwort:
java.lang.NoSuchFieldError: Die Klasse com.sun.tools.javac.tree.JCTree$JCImport verfügt nicht über das Mitgliedsfeld „com.sun.tools.javac.tree.JCTree qualid“
Lösung: Lombok auf 1.18.30 aktualisieren
Grund: https://github.com/projectlombok/lombok/issues/3393
Kompatibilitätsprüfung:
Da mein Projekt zuvor JDK17 verwendet hat, ist die Kompatibilität dieses Upgrades gut. Ich habe nur eines gefunden:
PopupMenu wurde in der Taskleiste verwendet und es ist ein Zeichensatzproblem aufgetreten:
ZGC war auch in früheren JDK-Versionen verfügbar, und diese Generation von ZGC ist noch vielversprechender. Die offizielle Einführung lautet wie folgt:
Anwendungen, die mit Generational ZGC ausgeführt werden, sollten folgende Vorteile bieten:
Geringeres Risiko von Zuteilungsstörungen,
Geringerer erforderlicher Heap-Speicheraufwand und
Geringerer CPU-Overhead für die Garbage Collection.
Aktivieren Sie Generational ZGC mit den Befehlszeilenoptionen -XX:+UseZGC -XX:+ZGenerational
Verwenden Sie mit Java21 Generations-ZGC
Ansicht der MooInfo-Speichernutzung
Das Obige ist nur eine vorläufige Erfahrung. Weitere Inhalte zu ZGC, wie beispielsweise detailliertes Generationsrecycling, werden in Zukunft weiter untersucht.
Die oben genannte Speichernutzung wird mit einem Tool überprüft, das ich zuvor erstellt habe, MooInfo:
https://github.com/rememberber/MooInfo
Virtuelle Threads sind leichtgewichtige Threads, die den Aufwand für das Schreiben, Warten und Debuggen gleichzeitiger Anwendungen mit hohem Durchsatz reduzieren.
Virtuelle Threads sind leichtgewichtige Threads, die den Aufwand für das Schreiben, Warten und Debuggen gleichzeitiger Anwendungen mit hohem Durchsatz reduzieren.
Ursprüngliche Einführung in Oracle: https://docs.oracle.com/en/java/javase/20/core/virtual-threads.html#GUID-DC4306FC-D6C1-4BCC-AECE-48C32C1A8DAA
Plattform-Thread
Maschinelle Übersetzung der offiziellen Oracle-Dokumentation:
Plattform-Threads werden als Thin Wrapper für Betriebssystem-Threads implementiert.
Plattform-Threads führen Java-Code auf ihren zugrunde liegenden Betriebssystem-Threads aus, und Plattform-Threads erfassen den Betriebssystem-Thread des Plattform-Threads während seiner gesamten Lebensdauer.
Daher ist die Anzahl der verfügbaren Plattform-Threads durch die Anzahl der Betriebssystem-Threads begrenzt.
Plattform-Threads verfügen normalerweise über einen großen Thread-Stack und andere vom Betriebssystem verwaltete Ressourcen.
Plattform-Threads unterstützen Thread-lokale Variablen.
Plattform-Threads eignen sich zum Ausführen aller Arten von Aufgaben, verfügen jedoch möglicherweise über begrenzte Ressourcen.
Maschinelle Übersetzung der offiziellen Oracle-Dokumentation:
Wie Plattform-Threads ist ein virtueller Thread eine Instanz von java.lang.Thread.
Virtuelle Threads sind jedoch nicht an bestimmte Betriebssystem-Threads gebunden.
Virtuelle Threads führen weiterhin Code auf Betriebssystem-Threads aus.
Wenn jedoch in einem virtuellen Thread ausgeführter Code einen blockierenden E/A-Vorgang aufruft, hält die Java-Laufzeit den virtuellen Thread an, bis er wieder aufgenommen werden kann.
Der Betriebssystem-Thread, der dem angehaltenen virtuellen Thread zugeordnet ist, kann nun Vorgänge für andere virtuelle Threads ausführen.
Umsetzungsprinzip
Virtuelle Threads werden ähnlich wie virtueller Speicher implementiert.
Um große Speichermengen zu simulieren, ordnet das Betriebssystem einen großen virtuellen Adressraum einer begrenzten Menge RAM zu.
Um eine große Anzahl von Threads zu simulieren, ordnet die Java-Laufzeitumgebung eine große Anzahl virtueller Threads einer kleinen Anzahl von Betriebssystem-Threads zu.
Im Gegensatz zu Plattform-Threads verfügen virtuelle Threads typischerweise über flache Aufrufstapel und führen nur einen einzelnen HTTP-Client-Aufruf oder eine einzelne JDBC-Abfrage aus.
Obwohl virtuelle Threads threadlokale Variablen unterstützen, sollten Sie deren Verwendung sorgfältig abwägen, da eine einzelne JVM möglicherweise Millionen virtueller Threads unterstützt.
虚拟线程适合运行大部分时间处于阻塞状态、通常等待 I/O 操作完成的任务。但是,它们不适用于长时间运行的 CPU 密集型操作。
虚拟线程用法
Thread thread = Thread.ofVirtual().start(() -> System.out.println("Hello"));
thread.join();
或者
try {
Thread.Builder builder = Thread.ofVirtual().name("MyThread");
Runnable task = () -> {
System.out.println("Running thread");
};
Thread t = builder.start(task);
System.out.println("Thread t name: " + t.getName());
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
或者
public class CreateNamedThreadsWithBuilders {
public static void main(String[] args) {
try {
Thread.Builder builder =
Thread.ofVirtual().name("worker-", 0);
Runnable task = () -> {
System.out.println("Thread ID: " +
Thread.currentThread().threadId());
};
// name "worker-0"
Thread t1 = builder.start(task);
t1.join();
System.out.println(t1.getName() + " terminated");
// name "worker-1"
Thread t2 = builder.start(task);
t2.join();
System.out.println(t2.getName() + " terminated");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
或者
try (ExecutorService myExecutor =
Executors.newVirtualThreadPerTaskExecutor()) {
Future<?> future =
myExecutor.submit(() -> System.out.println("Running thread"));
future.get();
System.out.println("Task completed");
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
以上是Java20文档的用法,实际使用时我发现还可以这样:
Thread.startVirtualThread(() -> {
// do something
});
平台线程和虚拟线程对比测试
为了测试对比,我建了一个项目
初步对比,和官网描述一致,计算密集型场景差别不大,IO密集型场景有明显改善:
虚拟线程100个,IO读文件
平台线程100个,IO读文件
虚拟线程100个,Get请求百度首页
平台线程100个,Get请求百度首页
但是由于是本地测试,且用例比较简陋,无法完全得出准确结论。
日后大家有实际IO密集性多线程场景可以实际感受下。
线程池?忘了它吧
开发人员通常会将应用程序代码从基于线程池的传统 ExecutorService 迁移到虚拟线程每任务 ExecutorService。
线程池和所有资源池一样,旨在共享昂贵的资源,
但虚拟线程并不昂贵,而且永远不需要将它们池化。
一个例子感受一下新特性:Record Patterns
before:
static void printSum(Object obj) {
if (obj instanceof Point p) {
int x = p.x();
int y = p.y();
System.out.println(x+y);
}
}
after:
static void printSum(Object obj) {
if (obj instanceof Point(int x, int y)) {
System.out.println(x+y);
}
}
本文分享自微信公众号 - 京东云开发者(JDT_Developers)。
如有侵权,请联系 [email protected] 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。