在之前的文章中,簡單描述了將spring boot 2.x升級spring boot 3.1的版本的過程。
本文將簡單介紹如何在spring 中引入虛擬線程,在文章最後會放上一些關於虛擬線程的官方參考資料。
JDK 22會引來重要特性,Virtual Threads也就是協程功能。
與主流的async、await方案(C#、JS等語言)相比,Java屬於stackfull coroutine有棧協程。
Java的虛擬線程API和舊版線程有良好的兼容性,升級成本非常低,還引入了結構化併發等多種工具類輔助開發人員更好的編程。
(再也不用羨慕go語言的go func(){} 了,現在Java也能只需要 Thread.startVirtualThread(() -> { }) )
在Spring中使用,你至少需要Spring 6以及版本。
如果是Maven構建的項目,需要加入預覽的編譯選項。虛擬線程是JDK 22的纔會正式發佈,大概是在今年(2023年)9月發佈。
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>21</source> <target>21</target> <compilerArgs> --enable-preview </compilerArgs> </configuration> </plugin> </plugins> </build>
增加配置類:
@EnableAsync @Configurationpublic class ThreadConfig { @Bean public AsyncTaskExecutor applicationTaskExecutor() { return new TaskExecutorAdapter(Executors.newVirtualThreadPerTaskExecutor()); } @Bean public TomcatProtocolHandlerCustomizer<?> protocolHandlerVirtualThreadExecutorCustomizer() { return protocolHandler -> { protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor()); }; } }
前往控制器測試,打印出來就是虛擬線程了:
@GetMapping("/name")
public String getThreadName() {
//VirtualThread[#171]/runnable@ForkJoinPool-1-worker-4
return Thread.currentThread().toString();
}
}
試一下JDK新提供的結構化併發工具包
package com.mycode; import jdk.incubator.concurrent.StructuredTaskScope; import java.time.Duration; import java.time.Instant; import java.util.Collections; import java.util.concurrent.*; import java.util.stream.Collectors; /** * https://download.java.net/java/early_access/loom/docs/api/java.base/java/lang/Thread.html * https://download.java.net/java/early_access/loom/docs/api/jdk.incubator.concurrent/jdk/incubator/concurrent/StructuredTaskScope.html */ public class StructuredConcurrency { static int taskNo = 10; static Callable<Integer> task = () -> { System.out.println(Thread.currentThread()); Thread.sleep(Duration.ofSeconds(1)); return 42; }; static void simpleScope() throws InterruptedException { try (var scope = new StructuredTaskScope<>()) { scope.fork(task); scope.join(); } } static void nestedScope() throws InterruptedException { try (var scopeOne = new StructuredTaskScope<>()) { Collections.nCopies(10, task).forEach(scopeOne::fork); try (var scopeTwo = new StructuredTaskScope<>()) { var scopeTwoTasks = Collections.nCopies(taskNo, task) .stream().map(scopeTwo::fork).toList(); scopeTwo.joinUntil(Instant.now().plusSeconds((long) (scopeTwoTasks.size() * 1.5))); } catch (TimeoutException e) { throw new RuntimeException(e); } scopeOne.join(); } } static void complexScope() { try (var scopeGlobal = new StructuredTaskScope<>("platform", Thread.ofPlatform().factory())) { var foo = scopeGlobal.fork(() -> { try (var scopeOne = new StructuredTaskScope.ShutdownOnFailure()) { var listOfFutures = Collections.nCopies(taskNo, task) .stream().map(scopeOne::fork).toList(); scopeOne.joinUntil(Instant.now().plusSeconds((long) (listOfFutures.size() * 1.5))); var results = listOfFutures.stream().map(Future::resultNow).toList(); System.out.println("completed in scopeOne"); return results; } catch (Throwable e) { throw new RuntimeException(e); } }); while (!foo.isDone()) Thread.sleep(10); var bar = scopeGlobal.fork(() -> foo.resultNow().parallelStream() .collect(Collectors.groupingBy(Integer::valueOf, Collectors.counting()))); scopeGlobal.join(); bar.resultNow(); } catch (InterruptedException e) { throw new RuntimeException(e); } } static void unboundForkInScope() { try (var scopeJoinUntil = new StructuredTaskScope<>()) { Collections.nCopies(15, task) .parallelStream().forEach(scopeJoinUntil::fork); scopeJoinUntil.joinUntil(Instant.now().plusSeconds(taskNo)); } catch (WrongThreadException | TimeoutException | InterruptedException e) { e.printStackTrace(); } } static void boundStream() throws InterruptedException { try (var scope = new StructuredTaskScope<Integer>()) { for (Callable<Integer> integerCallable : Collections.nCopies(100, task)) { scope.fork(integerCallable); } scope.join(); } } static void _streamInScope(boolean runParallelStream) { try (var scopeJoinUntil = new StructuredTaskScope<>()) { scopeJoinUntil.fork(() -> { var stream = Collections.nCopies(taskNo, task).stream(); if (runParallelStream) { stream = stream.parallel(); } return stream.map(scopeJoinUntil::fork).toList(); }); scopeJoinUntil.join(); } catch (WrongThreadException | InterruptedException e) { e.printStackTrace(); } } static void syncStreamInScope() { _streamInScope(false); } static void parallelStreamInScope() { _streamInScope(true); } public static void main(String[] args) throws InterruptedException { nestedScope(); simpleScope(); complexScope(); //unboundForkInScope(); Runnable runnable = () -> { System.out.println("run"); }; // Start a daemon thread to run a task Thread thread1 = Thread.ofPlatform().daemon().start(runnable); // Create an unstarted thread with name "duke", its start() method // must be invoked to schedule it to execute. Thread thread2 = Thread.ofPlatform().name("duke").unstarted(runnable); // A ThreadFactory that creates daemon threads named "worker-0", "worker-1", ... ThreadFactory factory = Thread.ofPlatform().daemon().name("worker-", 0).factory(); // Start a virtual thread to run a task Thread thread3 = Thread.ofVirtual().start(runnable); // A ThreadFactory that creates virtual threads ThreadFactory factory1 = Thread.ofVirtual().factory(); Thread.Builder.OfVirtual virtualThreadBuilder = Thread.ofVirtual() // 協程名稱 .name("fiber-1") // start > 0的情況下會覆蓋name屬性配置 .name("fiber-", 1L) // 是否啓用ThreadLocal .allowSetThreadLocals(false) // 是否啓用InheritableThreadLocal .inheritInheritableThreadLocals(false) // 設置未捕獲異常處理器 .uncaughtExceptionHandler((t, e) -> { }); Thread.Builder.OfPlatform platformThreadBuilder = Thread.ofPlatform() .daemon(true) .group(Thread.currentThread().getThreadGroup()) .name("thread-1") .name("thread-", 1L) .allowSetThreadLocals(false) .inheritInheritableThreadLocals(false) .priority(1) .stackSize(10) .uncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { } }); } // 創建平臺線程建造器,對應於Thread實例 public static Thread.Builder.OfPlatform ofPlatform() { return null; } // 創建虛擬線程建造器,對應於VirtualThread public static Thread.Builder.OfVirtual ofVirtual() { return null; } }
參考資料:
Working with Virtual Threads in Spring 6 | Baeldung
JDK新API結構並併發工具類相關文檔:
結構性併發: https://openjdk.org/jeps/453
範圍值(類似ThreadLocal): https://openjdk.org/jeps/446