Motivation: 正在try 迁移虚拟线程.

一百: 尽管虚拟线程很便宜,但操作系统可能会发现同时堆叠一些进程是可疑的,比如搜索网络上的IP或端口.

我使用下面的代码来限制线程的创建.TS_NetworkIPUtils TS_NetworkPortUtils

var executor = useVirtualThread
                    ? Executors.newVirtualThreadPerTaskExecutor()
                    : Executors.newFixedThreadPool(MAX_THREAD_COUNT());

是否可以创建一个Executor服务来创建虚拟线程并同时具有限制功能?

推荐答案

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.

Java相关问答推荐

Java 21虚拟线程会解决转向react 式单线程框架的主要原因吗?

为什么BasicComboBoxRenderer在文本不存在或文本为空的情况下设置两次文本?

Java函数式编程中的双值单值映射

DTO到实体,反之亦然,控制器和服务之间的哪一层应该处理转换?

CompleteableFuture是否运行在不同的内核上?

暂停计时器

为什么我的回收视图会显示重复的列表?

Java Telnet客户端重复的IAC符号

Instancio未在日志(log)中显示测试失败消息

Java中不兼容的泛型类型

如何创建模块信息类文件并将其添加到JAR中?

将关闭拍卖的TimerService

使IntelliJ在导入时优先 Select 一个类或将另一个标记为错误

如何在字节数组中反转UTF-8编码?

Oracle中从JSON中提取和插入数据

Java集合:NPE,即使没有添加空值

在具有Quarkus Panache的PostgreSQL中将JSON数据存储为JSONB时,会将其存储为转义字符串

Eureka客户端无法使用用户/通行证注册到Eureka服务器

如何在java中从以百分比表示的经过时间和结束日期中找到开始日期

为什么Java编译器为没有参数的方法(getter方法)创建桥接方法