diff --git a/.artifacts b/.artifacts
index 6565cbab5cb..a5f400f416d 100644
--- a/.artifacts
+++ b/.artifacts
@@ -140,3 +140,4 @@ dubbo-plugin-context
dubbo-plugin-classloader-filter
dubbo-plugin-proxy-bytebuddy
dubbo-plugin-qos-trace
+dubbo-plugin-loom
diff --git a/dubbo-plugin/dubbo-plugin-loom/pom.xml b/dubbo-plugin/dubbo-plugin-loom/pom.xml
new file mode 100644
index 00000000000..ba865fe8a69
--- /dev/null
+++ b/dubbo-plugin/dubbo-plugin-loom/pom.xml
@@ -0,0 +1,48 @@
+
+
+
+
+ org.apache.dubbo
+ dubbo-plugin
+ ${revision}
+ ../pom.xml
+
+ 4.0.0
+
+ com.aliyun.dubbo
+ dubbo-plugin-loom
+
+
+ 21
+ 21
+ UTF-8
+ UTF-8
+ false
+
+
+
+
+ org.apache.dubbo
+ dubbo-common
+ ${project.version}
+ true
+
+
+
diff --git a/dubbo-plugin/dubbo-plugin-loom/src/main/java/org/apache/dubbo/common/threadpool/support/loom/VirtualThreadPool.java b/dubbo-plugin/dubbo-plugin-loom/src/main/java/org/apache/dubbo/common/threadpool/support/loom/VirtualThreadPool.java
new file mode 100644
index 00000000000..c78c0b2b108
--- /dev/null
+++ b/dubbo-plugin/dubbo-plugin-loom/src/main/java/org/apache/dubbo/common/threadpool/support/loom/VirtualThreadPool.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.threadpool.support.loom;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.threadpool.ThreadPool;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_THREAD_NAME;
+import static org.apache.dubbo.common.constants.CommonConstants.THREAD_NAME_KEY;
+
+/**
+ * Creates a thread pool that use virtual thread
+ *
+ * @see Executors#newVirtualThreadPerTaskExecutor()
+ */
+public class VirtualThreadPool implements ThreadPool {
+ @Override
+ public Executor getExecutor(URL url) {
+ String name = url.getParameter(THREAD_NAME_KEY, (String) url.getAttribute(THREAD_NAME_KEY, DEFAULT_THREAD_NAME));
+ return Executors.newThreadPerTaskExecutor(
+ Thread.ofVirtual()
+ .name(name, 1)
+ .factory());
+ }
+}
diff --git a/dubbo-plugin/dubbo-plugin-loom/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.threadpool.ThreadPool b/dubbo-plugin/dubbo-plugin-loom/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.threadpool.ThreadPool
new file mode 100644
index 00000000000..e9f3348ad45
--- /dev/null
+++ b/dubbo-plugin/dubbo-plugin-loom/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.common.threadpool.ThreadPool
@@ -0,0 +1 @@
+virtual=org.apache.dubbo.common.threadpool.support.loom.VirtualThreadPool
diff --git a/dubbo-plugin/dubbo-plugin-loom/src/test/java/org/apache/dubbo/common/threadpool/support/loom/VirtualThreadPoolTest.java b/dubbo-plugin/dubbo-plugin-loom/src/test/java/org/apache/dubbo/common/threadpool/support/loom/VirtualThreadPoolTest.java
new file mode 100644
index 00000000000..42653cd8bd4
--- /dev/null
+++ b/dubbo-plugin/dubbo-plugin-loom/src/test/java/org/apache/dubbo/common/threadpool/support/loom/VirtualThreadPoolTest.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.threadpool.support.loom;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.threadpool.ThreadPool;
+
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledForJreRange;
+import org.junit.jupiter.api.condition.JRE;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+
+import static org.apache.dubbo.common.constants.CommonConstants.QUEUES_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.THREAD_NAME_KEY;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.startsWith;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class VirtualThreadPoolTest {
+
+ @Test
+ @EnabledForJreRange(min = JRE.JAVA_21)
+ void getExecutor1() throws Exception {
+ URL url = URL.valueOf("dubbo://10.20.130.230:20880/context/path?" +
+ THREAD_NAME_KEY + "=demo");
+ ThreadPool threadPool = new VirtualThreadPool();
+ Executor executor = threadPool.getExecutor(url);
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ executor.execute(() -> {
+ Thread thread = Thread.currentThread();
+ assertTrue(thread.isVirtual());
+ assertThat(thread.getName(), startsWith("demo"));
+ latch.countDown();
+ });
+
+ latch.await();
+ assertThat(latch.getCount(), is(0L));
+ }
+
+ @Test
+ @EnabledForJreRange(min = JRE.JAVA_21)
+ void getExecutor2() {
+ URL url = URL.valueOf("dubbo://10.20.130.230:20880/context/path?" + QUEUES_KEY + "=1");
+ ThreadPool threadPool = new VirtualThreadPool();
+ assertThat(threadPool.getExecutor(url).getClass().getName(), Matchers.is("java.util.concurrent.ThreadPerTaskExecutor"));
+ }
+}
diff --git a/dubbo-plugin/pom.xml b/dubbo-plugin/pom.xml
index a7cf59881f0..e919be86f28 100644
--- a/dubbo-plugin/pom.xml
+++ b/dubbo-plugin/pom.xml
@@ -63,4 +63,16 @@
test
+
+
+
+ loom
+
+ [21,)
+
+
+ dubbo-plugin-loom
+
+
+
diff --git a/dubbo-test/dubbo-test-modules/src/test/java/org/apache/dubbo/dependency/FileTest.java b/dubbo-test/dubbo-test-modules/src/test/java/org/apache/dubbo/dependency/FileTest.java
index c150a71088b..6059919675c 100644
--- a/dubbo-test/dubbo-test-modules/src/test/java/org/apache/dubbo/dependency/FileTest.java
+++ b/dubbo-test/dubbo-test-modules/src/test/java/org/apache/dubbo/dependency/FileTest.java
@@ -51,6 +51,7 @@ class FileTest {
ignoredModules.add(Pattern.compile("dubbo-core-spi"));
ignoredModules.add(Pattern.compile("dubbo-demo.*"));
ignoredModules.add(Pattern.compile("dubbo-annotation-processor"));
+ ignoredModules.add(Pattern.compile("dubbo-plugin-loom.*"));
ignoredArtifacts.add(Pattern.compile("dubbo-demo.*"));
ignoredArtifacts.add(Pattern.compile("dubbo-test.*"));