在Tomcat中启用虚拟线程特性( 二 )

该Servlet实现比较简单,就是在控制台打印一些虚拟线程和载体线程的一些信息,然后返回HTTP状态码为200和一个JSON字符展示当前精确到毫秒的时间 。接着编写一个main方法初始化Tomcat:
public class EmbedTomcatVirtualThreadDemo {    private static final String SERVLET_NAME = "VirtualThreadHandleServlet";    private static final String SERVLET_PATH = "/*";    /**     * 设置VM参数:     * --add-opens java.base/java.lang=ALL-UNNAMED     * --add-opens java.base/java.lang.reflect=ALL-UNNAMED     * --add-opens java.base/java.util.concurrent=ALL-UNNAMED     * -Djdk.tracePinnedThreads=full     *     * @param args args     * @throws Exception e     */    public static void main(String[] args) throws Throwable {        String pinMode = System.getProperty("jdk.tracePinnedThreads");        System.out.println("pin mode = " + pinMode);        Tomcat tomcat = new Tomcat();        Context context = tomcat.addContext("", (new File(".")).getAbsolutePath());        Tomcat.addServlet(context, SERVLET_NAME, new VirtualThreadHandleServlet());        context.addServletMappingDecoded(SERVLET_PATH, SERVLET_NAME);        Connector connector = new Connector();        ProtocolHandler protocolHandler = connector.getProtocolHandler();        if (protocolHandler instanceof AbstractProtocol<?> protocol) {            protocol.setAddress(InetAddress.getByName("127.0.0.1"));            protocol.setPort(9091);            ThreadFactory factory = Thread.ofVirtual().name("embed-tomcat-virtualWorker-", 0).factory();            Class<?> klass = Class.forName("java.util.concurrent.ThreadPerTaskExecutor");            MethodHandle methodHandle = MethodHandles.privateLookupIn(klass, MethodHandles.lookup())                    .findStatic(klass, "create", MethodType.methodType(klass, new Class[]{ThreadFactory.class}));            ExecutorService executor = (ExecutorService) methodHandle.invoke(factory);            protocol.setExecutor(executor);        }        tomcat.getService().addConnector(connector);        tomcat.start();    }}这里VirtualThreadHandleServlet匹配所有格式的请求路径并且处理所有请求方法类型的请求 。默认的虚拟线程调度器没有为虚拟线程设置名称,也就是如果使用Executors.newVirtualThreadPerTaskExecutor()作为Tomcat的线程池是最终调用看到的控制台输出的虚拟线程名称是一个空字符串 。所以笔者这里用MethodHandle直接实例化了默认修饰符没有开放访问权限的ThreadPerTaskExecutor类,基于一个自定义的ThreadFactory强制构造了一个自定义ThreadPerTaskExecutor实例 。调用main方法启动后见控制台输出:
【在Tomcat中启用虚拟线程特性】

在Tomcat中启用虚拟线程特性

文章插图
这里确认了Tomcat启动完成侦听127.0.0.1:9091,通过浏览器或者POSTMAN发送任意请求例如http://127.0.0.1:9091/foo就能看到响应结果和控制台输出:
在Tomcat中启用虚拟线程特性

文章插图
这里的Tomcat线程池甚至可以设计为一个完全自定义的虚拟线程调度器,可以参考前面一篇文章,这里不再赘述 。

推荐阅读