Есть ли способ узнать, на каком несущем потоке работает виртуальный поток?

Я впервые играю с Project Loom и у меня есть код

      try (var executor = Executors.newVirtualThreadExecutor()) {
    IntStream.range(0, 16).forEach(i -> {
        System.out.println("i = " + i + ", Thread ID = " + Thread.currentThread());
        executor.submit(() -> {
            System.out.println("Thread ID = " + Thread.currentThread());
        });
    });
}

с выходом вроде

      Thread ID = VirtualThread[#37]/runnable@ForkJoinPool-1-worker-4
Thread ID = VirtualThread[#33]/runnable@ForkJoinPool-1-worker-5
i = 9, Thread ID = Thread[#1,main,5,main]
Thread ID = VirtualThread[#43]/runnable@ForkJoinPool-1-worker-9
Thread ID = VirtualThread[#46]/runnable@ForkJoinPool-1-worker-11
i = 10, Thread ID = Thread[#1,main,5,main]
i = 11, Thread ID = Thread[#1,main,5,main]

Есть ли способ узнать, на каком несущем потоке работает каждый виртуальный поток?

Делает ForkJoinPool-1-worker-11 представляют конкретный поток-носитель (платформу) или это означает что-то еще?

2 ответа

Да, этот суффикс является именем текущего потока носителя.

Когда я использую следующий код

      public static void main(String[] args) throws InterruptedException {
    Set<String> threadStrings = ConcurrentHashMap.newKeySet();
    try(var executor = Executors.newVirtualThreadPerTaskExecutor()) {
        executor.invokeAll(Collections.nCopies(16,
            () -> threadStrings.add(Thread.currentThread().toString())));
    }
    System.out.println("\tSimple Run");
    threadStrings.stream().sorted().forEachOrdered(System.out::println);

    threadStrings.clear();

    try(var executor = Executors.newVirtualThreadPerTaskExecutor()) {
        executor.invokeAll(Collections.nCopies(16, () -> {
            threadStrings.add(Thread.currentThread().toString());
            Thread.sleep(100);
            return threadStrings.add(Thread.currentThread().toString());
        }));
    }
    System.out.println("\tWith wait");
    threadStrings.stream().sorted().forEachOrdered(System.out::println);
}

Он печатает

              Simple Run
VirtualThread[#15]/runnable@ForkJoinPool-1-worker-1
VirtualThread[#17]/runnable@ForkJoinPool-1-worker-2
VirtualThread[#18]/runnable@ForkJoinPool-1-worker-3
VirtualThread[#19]/runnable@ForkJoinPool-1-worker-4
VirtualThread[#20]/runnable@ForkJoinPool-1-worker-1
VirtualThread[#21]/runnable@ForkJoinPool-1-worker-1
VirtualThread[#22]/runnable@ForkJoinPool-1-worker-4
VirtualThread[#23]/runnable@ForkJoinPool-1-worker-1
VirtualThread[#24]/runnable@ForkJoinPool-1-worker-4
VirtualThread[#25]/runnable@ForkJoinPool-1-worker-4
VirtualThread[#26]/runnable@ForkJoinPool-1-worker-1
VirtualThread[#27]/runnable@ForkJoinPool-1-worker-4
VirtualThread[#28]/runnable@ForkJoinPool-1-worker-4
VirtualThread[#29]/runnable@ForkJoinPool-1-worker-1
VirtualThread[#30]/runnable@ForkJoinPool-1-worker-4
VirtualThread[#31]/runnable@ForkJoinPool-1-worker-4
        With wait
VirtualThread[#36]/runnable@ForkJoinPool-1-worker-2
VirtualThread[#37]/runnable@ForkJoinPool-1-worker-3
VirtualThread[#37]/runnable@ForkJoinPool-1-worker-8
VirtualThread[#38]/runnable@ForkJoinPool-1-worker-4
VirtualThread[#38]/runnable@ForkJoinPool-1-worker-8
VirtualThread[#39]/runnable@ForkJoinPool-1-worker-1
VirtualThread[#39]/runnable@ForkJoinPool-1-worker-8
VirtualThread[#40]/runnable@ForkJoinPool-1-worker-5
VirtualThread[#40]/runnable@ForkJoinPool-1-worker-8
VirtualThread[#41]/runnable@ForkJoinPool-1-worker-6
VirtualThread[#41]/runnable@ForkJoinPool-1-worker-8
VirtualThread[#42]/runnable@ForkJoinPool-1-worker-7
VirtualThread[#42]/runnable@ForkJoinPool-1-worker-8
VirtualThread[#43]/runnable@ForkJoinPool-1-worker-5
VirtualThread[#43]/runnable@ForkJoinPool-1-worker-8
VirtualThread[#44]/runnable@ForkJoinPool-1-worker-1
VirtualThread[#44]/runnable@ForkJoinPool-1-worker-8
VirtualThread[#45]/runnable@ForkJoinPool-1-worker-5
VirtualThread[#45]/runnable@ForkJoinPool-1-worker-6
VirtualThread[#46]/runnable@ForkJoinPool-1-worker-5
VirtualThread[#46]/runnable@ForkJoinPool-1-worker-8
VirtualThread[#47]/runnable@ForkJoinPool-1-worker-2
VirtualThread[#49]/runnable@ForkJoinPool-1-worker-1
VirtualThread[#49]/runnable@ForkJoinPool-1-worker-8
VirtualThread[#50]/runnable@ForkJoinPool-1-worker-2
VirtualThread[#50]/runnable@ForkJoinPool-1-worker-6
VirtualThread[#51]/runnable@ForkJoinPool-1-worker-3
VirtualThread[#51]/runnable@ForkJoinPool-1-worker-5
VirtualThread[#52]/runnable@ForkJoinPool-1-worker-2
VirtualThread[#52]/runnable@ForkJoinPool-1-worker-8

(результаты могут отличаться)

демонстрация того, как нить носителя может измениться при выполнении sleep. Но в текущем снимке («сборка 18-ткацкого станка+6-282») нет возможности указать свой Executorбольше, и нет никакого метода для запроса виртуального потока об используемом им потоке-носителе (кроме неявной подсказки через toString()). Таким образом, управление базовыми потоками хоста в этой версии в основном представляет собой черный ящик.

Имейте в виду, что это постоянное развитие. Неясно, изменится ли это и как.

На заметку о вашем многоразовом методе сна :)_

Назовите свои виртуальные потоки «virtualThread- », а затем убедитесь, что имя начинается с «virtualThread- »;https://davidvlijmincx.com/posts/naming-virtual-threads/

Другие вопросы по тегам