Speaker: Todd Ginsberg
Bluesky: @todd.ginsberg.com
For more see the table of contents
Project Loom
Project charter includes;
- easy to use
- high throughput
- lightweight concurrency
- new programming models on the Java platform
Virtual Threads
- Platform threads in JVM map to OS threads. Not useful when blocked, memory hungry, limited number by OS, etc
- Virtual threads have nothing to do with OS. Just memory on heap.
- When virtual threads have work, mounted to carrier thread.
- Carrier thread uses OS thread
- Virtual threads still java.lang.Thread, must lower memory requirements, number limited by heap memory, quick to create, better use of system resources
- Virtual threads have ids, but not names, by default since you are supposed to use them and then throw away.
- 2 seconds to create thousands of platform threads. 41 milliseconds to do the same for virtual threads. 368 milliseconds to create a million virtual threads
- Little’s law: concurrency – arrival rate (aka throughput) * latency. Virtual threads increase thoroughput
- Do not pool virtual threads. Create, use, expose. You wouldn’t pool other inexpensive objects.
Structured Concurrency
- API change so still preview in Java 25
- Suppose have two futures. One that takes 2 seconds and one that takes 4 seconds.
- Want to kill one when the other fails so not wasting time.
- While think of as parent/child threads normally, that relationship doesn’t actually exist
- jps command gives process ids
- To get thread dump: jcmd <main program process id> Thread.dump_to_file -format=json unstructured.json
- Goals: promote style of concurrent programming to eliminate common risks, improve concurrency
- Enforces children don’t outlive parents
- Explicit relationship between tasks and subtasks, observability is easier, managing work is easier
- join() – join point waits until all tasks are done and can then interpret results.
- Create StructuredTaskScope.open() in try with resources which means all or nothing. Whole scope succeeds or fails
- scope.fork(() -> doWork())
- scope.join()
- future.get() to get the answer now that the join is done
- Can nest scopes
Scoped Values
- in Java 25 (no longer in preview)
- ThreadLocal let you set data. Problems: unconstrained mutability (anyone who can read to it can write to it), unbounded lifespan (have to clean up if reusing platform thread), expensive inheritance
- Scoped values: Immutable, defined lifetime, cheap/free inheritance
- Ex: static ScopedValue<String> SCOPED. and ScopedValue.where(SCOPED, obj).run(() -> …)
- Scoped values good for passing data one way. Good when have structured sharing use cases – ex: data many layers way from where you create it
- Can replace one way ThreadLocal as use case without structured concurrency
My take
Not the point of the talk but I like that he uses Duration.ofMillis() instead of just putting a number. This topic is like pipelines; I needed to hear it a few times from different people for it to click. Given that scope values are in the Java 25 LTS and structured concurrency is not, I was curious how to use scope values alone so nice to hear that.