tl;dr
是否可以创建一个执行器服务,用于创建虚拟线程并同时具有限制功能?
是的
堆积many virtual threads个,被阻止,而waiting for a counting semaphore个由当前执行的有限数量的任务中的任何一个释放.
Details
我使用下面的代码来限制线程的创建.TS_网络IPUtils
不,不限制线程数.虚拟线程的设计非常"便宜",默认情况下几乎不占用系统资源(内存、CPU).您可能一次可以支持millions个虚拟线程.
当您的任务开销很大时,您可能需要限制同时执行的任务的数量.继续并堆叠许多线程,但阻止它们的执行,以等待一定数量的并发任务忙于执行.在虚拟线程上阻塞是非常便宜的(他们有raison d’être个),所以如果它们堆积在一起也没有问题.
使用计数信号量来限制并发任务
您可以使用counting semaphore来限制虚拟线程中同时运行的任务的数量.参见Cay Horstmann所著的教程Virtual Threads中的第Rate Limiting节.
类似于下面的示例.我们提交了15个任务,但同时执行的任务限制在5个或更少.这new Semaphore ( 5 )
个许可证提供了一个由五个许可证组成的桶,可以获得和释放.许可证就像一本书从图书馆借了总共五本,最后还给别人借--有很多借款人,但一次只有五人.
注意后面的任务是如何等待计数信号量的许可的.
最后,在完成所有提交的任务后,我们退出Try-With-Resources语法块.
(警告:我只是简单地写出了这个代码示例,并没有考虑太多.我的目标是比链接的Cay Horstmann教程中看到的示例更简单.)
package work.basil.example.threading;
import java.time.Duration;
import java.time.Instant;
import java.util.SequencedCollection;
import java.util.concurrent.*;
import java.util.concurrent.locks.LockSupport;
public class LimitTasks
{
public static void main ( String[] args )
{
LimitTasks app = new LimitTasks ( );
app.demo ( );
}
private void demo ( )
{
System.out.println ( "Demo start. " + Instant.now ( ) );
SequencedCollection < String > log = new CopyOnWriteArrayList <> ( ); // System.out.println calls across threads do *NOT* necessarily appear on console in chronological order.
final Semaphore TASKS_PERMITS = new Semaphore ( 5 );
log.add ( "Permits available: " + TASKS_PERMITS.availablePermits ( ) );
try (
ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor ( ) ;
)
{
for ( int nthTask = 1 ; nthTask <= 15 ; nthTask++ )
{
executorService.submit ( new PrintIt ( nthTask , TASKS_PERMITS , log ) );
}
}
log.add ( "After try-with-resources on ExecutorService. " + Instant.now ( ) );
log.forEach ( System.out :: println );
System.out.println ( "Demo done. " + Instant.now ( ) );
}
}
class PrintIt implements Runnable
{
private final int label;
private final Semaphore tasksPermits;
private final SequencedCollection < String > log;
public PrintIt ( final int label , final Semaphore tasksPermits , final SequencedCollection < String > log )
{
this.label = label;
this.tasksPermits = tasksPermits;
this.log = log;
}
@Override
public void run ( )
{
this.log.add ( "nthTask " + label + ". Approximately " + this.tasksPermits.getQueueLength ( ) + " tasks waiting for a permit. " + Instant.now ( ) );
long startNanos = System.nanoTime ( );
try
{
this.tasksPermits.acquire ( );
long elapsed = System.nanoTime ( ) - startNanos ;
this.log.add ( "nthTask " + label + " waited " + Duration.ofNanos ( elapsed ) + " for a permit. Permits available: " + this.tasksPermits.availablePermits ( ) + ". " + Instant.now ( ) );
long low = Duration.ofSeconds ( 2 ).toNanos ( );
long high = Duration.ofSeconds ( 10 ).toNanos ( );
long random = ThreadLocalRandom.current ( ).nextLong ( low , high );
LockSupport.parkNanos ( random );
this.log.add ( "nthTask " + label + " running at " + Instant.now ( ) + ". Permits available: " + this.tasksPermits.availablePermits ( ) + "." );
} catch ( InterruptedException e )
{
throw new RuntimeException ( e );
} finally
{
this.tasksPermits.release ( );
}
}
}
当运行在带有来自IntelliJ IDEA 2023.3.3(旗舰版)Apple Silicon的Mac上的Java 21上时.
Demo start. 2024-02-06T04:42:12.414617Z
Permits available: 5
nthTask 3. Approximately 0 tasks waiting for a permit. 2024-02-06T04:42:12.425896Z
nthTask 4. Approximately 0 tasks waiting for a permit. 2024-02-06T04:42:12.425813Z
nthTask 7. Approximately 0 tasks waiting for a permit. 2024-02-06T04:42:12.427279Z
nthTask 1. Approximately 0 tasks waiting for a permit. 2024-02-06T04:42:12.425813Z
nthTask 2. Approximately 0 tasks waiting for a permit. 2024-02-06T04:42:12.425813Z
nthTask 9. Approximately 0 tasks waiting for a permit. 2024-02-06T04:42:12.428066Z
nthTask 10. Approximately 0 tasks waiting for a permit. 2024-02-06T04:42:12.428092Z
nthTask 8. Approximately 0 tasks waiting for a permit. 2024-02-06T04:42:12.427894Z
nthTask 5. Approximately 0 tasks waiting for a permit. 2024-02-06T04:42:12.425983Z
nthTask 6. Approximately 0 tasks waiting for a permit. 2024-02-06T04:42:12.426432Z
nthTask 11. Approximately 5 tasks waiting for a permit. 2024-02-06T04:42:12.435172Z
nthTask 12. Approximately 5 tasks waiting for a permit. 2024-02-06T04:42:12.435178Z
nthTask 13. Approximately 7 tasks waiting for a permit. 2024-02-06T04:42:12.435214Z
nthTask 14. Approximately 7 tasks waiting for a permit. 2024-02-06T04:42:12.435225Z
nthTask 15. Approximately 9 tasks waiting for a permit. 2024-02-06T04:42:12.435261Z
nthTask 3 waited PT0.0088205S for a permit. Permits available: 0. 2024-02-06T04:42:12.434720Z
nthTask 2 waited PT0.008820167S for a permit. Permits available: 1. 2024-02-06T04:42:12.434640Z
nthTask 4 waited PT0.008743834S for a permit. Permits available: 1. 2024-02-06T04:42:12.434705Z
nthTask 7 waited PT0.007267583S for a permit. Permits available: 1. 2024-02-06T04:42:12.434666Z
nthTask 1 waited PT0.008801666S for a permit. Permits available: 2. 2024-02-06T04:42:12.434619Z
nthTask 2 running at 2024-02-06T04:42:15.306355Z. Permits available: 0.
nthTask 9 waited PT2.887408834S for a permit. Permits available: 0. 2024-02-06T04:42:15.315471Z
nthTask 3 running at 2024-02-06T04:42:17.420189Z. Permits available: 0.
nthTask 10 waited PT4.9929405S for a permit. Permits available: 0. 2024-02-06T04:42:17.421040Z
nthTask 7 running at 2024-02-06T04:42:17.920249Z. Permits available: 0.
nthTask 8 waited PT5.493109666S for a permit. Permits available: 0. 2024-02-06T04:42:17.921006Z
nthTask 4 running at 2024-02-06T04:42:18.204203Z. Permits available: 0.
nthTask 5 waited PT5.779108792S for a permit. Permits available: 0. 2024-02-06T04:42:18.205082Z
nthTask 9 running at 2024-02-06T04:42:19.479390Z. Permits available: 0.
nthTask 6 waited PT7.053674S for a permit. Permits available: 0. 2024-02-06T04:42:19.480079Z
nthTask 1 running at 2024-02-06T04:42:19.800778Z. Permits available: 0.
nthTask 11 waited PT7.366630625S for a permit. Permits available: 0. 2024-02-06T04:42:19.801791Z
nthTask 10 running at 2024-02-06T04:42:20.015210Z. Permits available: 0.
nthTask 12 waited PT7.580567167S for a permit. Permits available: 0. 2024-02-06T04:42:20.015699Z
nthTask 11 running at 2024-02-06T04:42:24.640389Z. Permits available: 0.
nthTask 13 waited PT12.205975708S for a permit. Permits available: 0. 2024-02-06T04:42:24.641142Z
nthTask 8 running at 2024-02-06T04:42:26.301821Z. Permits available: 0.
nthTask 14 waited PT13.867504542S for a permit. Permits available: 0. 2024-02-06T04:42:26.302663Z
nthTask 12 running at 2024-02-06T04:42:26.407675Z. Permits available: 0.
nthTask 15 waited PT13.972995459S for a permit. Permits available: 0. 2024-02-06T04:42:26.408182Z
nthTask 5 running at 2024-02-06T04:42:27.728516Z. Permits available: 0.
nthTask 6 running at 2024-02-06T04:42:28.044373Z. Permits available: 1.
nthTask 15 running at 2024-02-06T04:42:29.974056Z. Permits available: 2.
nthTask 13 running at 2024-02-06T04:42:30.613859Z. Permits available: 3.
nthTask 14 running at 2024-02-06T04:42:33.088984Z. Permits available: 4.
After try-with-resources on ExecutorService. 2024-02-06T04:42:33.089508Z
Demo done. 2024-02-06T04:42:33.094512Z
Process finished with exit code 0
有关虚拟话题的更多信息,请参见罗恩·普雷斯勒、艾伦·贝特曼或何塞·保马德的精彩视频演讲.读一读JEP 444.