虚拟线程是什么
虚拟线程是与原来的平台线程类似的线程,它也是Java.Lang.Thread的一个实例,但它是由Jvm进行管理和调度的。
与虚拟内存的实现方式类似,在Jvm中会存在一个Map来维护虚拟线程与实际系统线程的对应关系。
当虚拟线程运行时,Jvm会把它分配到一个平台线程上,这个平台线程被称为Carrier。当虚拟线程遇到I/O阻塞被挂起后,这个Carrier就空闲下来,Jvm会分配其他的虚拟线程到这个Carrier上。
根据虚拟线程的特性来说,它适合执行一些耗时的I/O阻塞式的任务。
虚拟线程与平台线程的区别是什么
- 管理与调度不同:平台线程是由OS进行管理和调度的,而虚拟线程则是由JVM进行管理与调度的。
- 线程规模不同:平台线程的规模受到OS的限制,而虚拟线程则没有这个限制,理论上来说虚拟线程的最大数量要比平台线程大得多。
-
使用成本不同:由于虚拟内存是受JVM管理的,因此它的分配不需要进行系统调用,也不受系统上下文切换的影响。
什么场景下使用虚拟线程
虚拟线程适合在高并发场景下,执行可能带来长时间I/O阻塞的任务。官方给出的示例是一个Server-Client模式的例子。
public class EchoServer { public static void main(String[] args) throws IOException { if (args.length != 1) { System.err.println("Usage: java EchoServer <port>"); System.exit(1); } int portNumber = Integer.parseInt(args[0]); try ( ServerSocket serverSocket = new ServerSocket(Integer.parseInt(args[0])); ) { while (true) { Socket clientSocket = serverSocket.accept(); // Accept incoming connections // Start a service thread Thread.ofVirtual().start(() -> { try ( PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true); BufferedReader in = new BufferedReader( new InputStreamReader(clientSocket.getInputStream())); ) { String inputLine; while ((inputLine = in.readLine()) != null) { System.out.println(inputLine); out.println(inputLine); } } catch (IOException e) { e.printStackTrace(); } }); } } catch (IOException e) { System.out.println("Exception caught when trying to listen on port " + portNumber + " or listening for a connection"); System.out.println(e.getMessage()); } } } public class EchoClient { public static void main(String[] args) throws IOException { if (args.length != 2) { System.err.println( "Usage: java EchoClient <hostname> <port>"); System.exit(1); } String hostName = args[0]; int portNumber = Integer.parseInt(args[1]); try ( Socket echoSocket = new Socket(hostName, portNumber); PrintWriter out = new PrintWriter(echoSocket.getOutputStream(), true); BufferedReader in = new BufferedReader( new InputStreamReader(echoSocket.getInputStream())); ) { BufferedReader stdIn = new BufferedReader( new InputStreamReader(System.in)); String userInput; while ((userInput = stdIn.readLine()) != null) { out.println(userInput); System.out.println("echo: " + in.readLine()); if (userInput.equals("bye")) break; } } catch (UnknownHostException e) { System.err.println("Don't know about host " + hostName); System.exit(1); } catch (IOException e) { System.err.println("Couldn't get I/O for the connection to " + hostName); System.exit(1); } } }
如何使用虚拟线程
官方提供了多种使用虚拟线程的方式:
Thread.ofVirtual()
Thread thread = Thread.ofVirtual().start(() -> System.out.println("Hello"));
thread.join();
Thread.Builder()
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();
}
Executors.newVirtualThreadPerTaskExecutor()
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();
}
虚拟线程 VS 平台线程
线程执行100个sleep 1秒的任务
var vs = Executors.newFixedThreadPool(200);
List<Future<Integer>> futures = new ArrayList<>();
var begin = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
var future = vs.submit(() -> {
Thread.sleep(1000L);
return a.addAndGet(1);
});
futures.add(future);
}
耗时结果对比
Platform Thread Exec time: 5050ms.
Virtual Thread Exec time: 1039ms.
使用虚拟线程的注意事项
- 直接使用虚拟线程,而不要像使用平台线程那样进行池化。
- 虽然没有了OS的限制,可以创造出大量的虚拟线程,但是要注意,对于有限资源的访问(如数据库)等还是要加以限制。
- 避免使用
synchronized
阻塞操作。
除非注明,否则均为哦豁原创文章,转载必须以链接形式标明本文链接