Trong bài viết Khởi tạo và chạy một thread trong Java, mình đã giới thiệu với các bạn về class Thread của Java để tạo mới một thread. Thread tạo bởi class Thread này được gọi là platform thread và số lượng platform thread có thể tạo bị hạn chế, phụ thuộc vào cấu hình của máy mà các bạn đang sử dụng để chạy ứng dụng Java. Từ phiên bản 19, Java giới thiệu một tính năng mới gọi là virtual thread cho phép chúng ta có thể tạo một số lượng lớn các thread, sử dụng tương tự như platform thread. Virtual thread sẽ được quản lý bởi JRE, hữu ích trong các trường hợp xử lý blocking IO. Cụ thể như thế nào? Chúng ta sẽ cùng nhau tìm hiểu trong bài viết này các bạn nhé!
Đầu tiên, để phân biệt giữa việc khởi tạo platform thread và virtual thread, Java giới thiệu 2 phương thức static() trong class Thread là ofVirtual() và ofPlatform() để khởi tạo 2 loại thread này.
Ví dụ mình có một tác vụ được implement sử dụng interface Runnable với nội dung như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package com.huongdanjava.java; public class Calculator implements Runnable { public void run() { System.out.println("Calculator : " + Thread.currentThread().getName()); int a = 2; int b = 7; int sum = a + b; System.out.println(sum); } } |
Mình có thể khởi tạo virtual thread và platform thread để submit tác vụ này như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package com.huongdanjava.java; public class Application { public static void main(String[] args) throws InterruptedException { Thread.ofVirtual().start(new Calculator()); Thread.ofPlatform().start(new Calculator()); Thread.sleep(3000); } } |
Kết quả:
Như các bạn thấy, cả 2 loại thread đều có thể thực thi tác vụ và trả về kết quả như nhau.
Trong class Calculator trên, mình có dòng code để in ra tên của Thread đang xử lý tác vụ nhưng như các bạn thấy, đối với virtual thread thì chúng ta không lấy được tên của Thread này. Đó là bởi vì, mặc định virtual thread không được định nghĩa name. Các bạn có thể định nghĩa name cho virtual thread như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package com.huongdanjava.java; public class Application { public static void main(String[] args) throws InterruptedException { Thread.ofVirtual().name("Huong Dan Java Virtual Thread").start(new Calculator()); Thread.ofPlatform().start(new Calculator()); Thread.sleep(3000); } } |
Lúc này các bạn sẽ thấy tên của virtual thread được hiển thị như sau:
Các bạn cũng có thể tạo mới virtual thread sử dụng các phương thức static startVirtualThread() của class Thread hoặc newVirtualThreadPerTaskExecutor() của class Executors.
Phương thức static startVirtualThread() của class Thread sẽ khởi tạo mới virtual thread và gọi phương thức start() để thực thi tác vụ luôn. Mình có thể tạo mới virtual thread và submit tác vụ vào virtual thread này với phương thức startVirtualThread() như sau:
1 2 3 4 5 6 7 8 9 10 11 |
package com.huongdanjava.java; public class Application { public static void main(String[] args) throws InterruptedException { Thread.startVirtualThread(new Calculator()); Thread.sleep(3000); } } |
Chạy ứng dụng, các bạn sẽ thấy kết quả như sau:
Sử dụng phương thức static newVirtualThreadPerTaskExecutor() của class ExecutorService, các bạn cũng có thể làm điều tương tự:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
package com.huongdanjava.java; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Application { public static void main(String[] args) throws InterruptedException { ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor(); executorService.submit(new Calculator()); Thread.sleep(3000); } } |
Kết quả:
Như mình đã nói ở trên, chúng ta có thể tạo mới một số lượng lớn các virtual thread thay vì bị hạn chế như platform thread.
Cùng một đoạn code, nếu sử dụng platform thread, các bạn chỉ có thể tạo mới số lượng thread nhất định, ví dụ như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
package com.huongdanjava.java; public class Application { public static void main(String[] args) throws InterruptedException { for (int i = 0; i< 1_000_000; i++) { System.out.println("Creating thread " + i); new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(60000); } catch (InterruptedException e) { throw new RuntimeException(e); } } }).start(); } Thread.sleep(60000); } } |
Trong đoạn code trên, mình loop 1 triệu lần và mỗi lần loop mình sẽ tạo mới một platform thread và start nó. Với máy của mình thì mình chỉ có thể tạo 2025 platform thread, và bị lỗi out of memory sau đó, như sau:
Nếu bây giờ mình sử dụng virtual thread thay thế:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
package com.huongdanjava.java; public class Application { public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 1_000_000; i++) { System.out.println("Creating thread " + i); Thread.startVirtualThread(new Runnable() { @Override public void run() { try { Thread.sleep(60000); } catch (InterruptedException e) { throw new RuntimeException(e); } } }); } Thread.sleep(60000); } } |
các bạn sẽ thấy mình có thể tạo mới 1 triệu virtual thread mà không có vấn đề gì cả:
Về bản chất bên trong thì có một pool của platform thread dùng để chạy virtual thread.
Tác vụ của chúng ta được execute sử dụng platform thread, thông qua virtual thread.
Khi thực hiện một tác vụ blocking IO, để tránh block platform thread, virtual thread nên được sử dụng.
Virtual thread được execute sử dụng class Continuation. Class Continuation này sử dụng phương thức run() để execute tác vụ của chúng ta trong virtual thread. Trong lúc chờ một tác vụ blocking IO hoàn thành, virtual thread sẽ đưa tác vụ của chúng ta lưu trong heap memory, release platform thread mà nó đang sử dụng để cho những tác vụ khác sử dụng.
Khi tác vụ IO hoàn thành, một signal sẽ được trigger để call Continuation.run(). Continuation.run() này sẽ lấy stack của virtual thread từ heap memory và đưa nó vào danh sách đợi của platform thread để lấy thread và resume tác vụ. Thread mới có thể khác với thread ban đầu mà tác vụ sử dụng để chạy.
Chỉ nên sử dụng virtual thread cho các tác vụ blocking IO, không nên sử dụng với các tác vụ tính toán các bạn nhé!