Butcher virtual threads like a pro!


Piotr Przybył

conference logo
17 IV 2024

© 2024 Piotr Przybył. Licensed under CC BY-NC-SA 4.0
Java and OpenJDK are trademarks or registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners.
Picture by Pete Linforth from Pixabay

$ whoami

Piotr Przybył
piotrprz
@piotrprz@mstdn.social
Senior Developer Advocate
Elastic logo
Java Champion
Testcontainers Community Champion
Oracle ACE Associate
Trainer
SoftwareGarden.dev

$ who are you

CAVEAT AVDITORES!

A.K.A. Safe harbour statement: don't assume anything based on this presentation. Verify on your own. Errare humanum est.

Preview features

JEP-12

--enable-preview

More on preview features

Concurrency

platform / OS threads can be too slow to create

in limited number, hence no thread-per-request

reactive is not easy to debug and troubleshoot

lack of async / await

Virtual Threads / Loom

Biggest change since Lambda?

thinkspin

Virtual Threads

standard feature in 2️⃣1️⃣

JEP-444

Structured Concurrency

🧪preview feature in 2️⃣2️⃣

JEP-462

Scoped Values

🧪 preview feature in 2️⃣2️⃣

JEP-464

Corner stone: Virtual Threads

not reinvent the wheel, keep threads concept

cheap to start

carried by platform / OS threads under the hood

unmounting carrier threads when waiting, e.g. for IO

always daemons, with normal prio

Picture by Pexels from Pixabay

There is no magic switch to turn the Threads you currently have into Virtual Threads

You need to alter your source code

  • Thread.ofVirtual(Runnable r).start()
  • Thread.startVirtualThread(Runnable r)
  • Executors.newVirtualThreadPerTaskExecutor()
  • Thread.ofVirtual().factory()

Some frameworks allow easy switch to virtual threads

Virtual Threads will not magically squeeze more juice from your CPU!

But your application will scale better ;-)

What not to do with Virtual Threads

  • reuse
  • pool
  • pin

-Djdk.tracePinnedThreads=full
and JFR events

Testing (if there are no) pinned Virtual Threads

How many lights are there?

when you don't look at photons they behave differently
Don't look at virtual threads, or else...!
Picture by StockSnap from Pixabay

Structured concurrency

  • better "idioms" for multi-threaded code
  • helps to eliminate thread leaks and cancellation delays
  • not replacing interruption with cancellation (for now)

Structured concurrency



        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
          var namesSubtask = scope.fork(()-> nameService.getNames());
          var scoresSubtask = scope.fork(()-> scoreService.getScores());
          scope.join();
          scope.throwIfFailed();
          var names = namesSubtask.resultNow();
          var scores = scoresSubtask.resultNow();
          return combine(names, scores);
        }

        

free cancellation within the tree, yay!

Structured concurrency


            StructuredTaskScope.ShutdownOnFailure()
            StructuredTaskScope.ShutdownOnSuccess<T>()
        

don't forget join/joinUntil

and that the subtasks should be interruptible!

Scoped Values

one-way immutable "ThreadLocals"
+
bounded lifetime (visible in code)

=
simplified reasoning
and improved performance

(ThreadLocal doesn't change and doesn't get deprecated)

Scoped Values


        static final ScopedValue<Level> SECURITY_CLEARANCE_LEVEL =
                                        ScopedValue.newInstance();
        // ...
        ScopedValue.where(SECURITY_CLEARANCE_LEVEL, levelFromRequest())
          .run(() -> {
            var level = SECURITY_CLEARANCE_LEVEL.orElse(guestLevel());
            if (level.permits()) {
                doSomeStuff();
                doSomeOtherStuff();
            } else {
                error();
            }
            ScopedValue.where(SECURITY_CLEARANCE_LEVEL, NO_ACCESS_LEVEL)
              .run(() -> log(progress);
          });

        

Scoped Values

  • work nicely with Structured Concurrency
  • run(Runnable r) or call(Callable c)
  • where(...) can be nested with its own scope

as long as you don't mutate the darn thing!

So how can we butcher
virtual threads & Co.?

  • use VTs mainly for CPU heavy tasks
  • swarm tasks without any flow control
  • ignore unmounting for IO
  • pool, reuse and mix VTs with synchronized
  • ignore interruptions
  • mutate scoped values

Always

Everyone

Feedback

Always give feedback

How was it?

POLL-LINK

piotrprz

@piotrprz@mstdn.social

Slides


SLIDES-LINK

Butcher virtual threads like a pro!


Thank you

Merci !

Piotr Przybył
piotrprz
piotrprz@mstdn.social
https://SoftwareGarden.dev
SLIDES-LINK
CODE-LINK