java 21
Abstract
- project Loom: virtual threads + structured concurrency (preview)
- GC ZGCC: pause in microseconds vs milliseconds with drawbacks less throughput and more memory consumption
- project Panama: better support with native code
- project Amber: Java language quality of life
String templates (preview)
Abstract
- simplify string concatenation
- enable domain-specific processing
- incentivize the “right way”
Template processor STR
String form = STR."""
Desc Unit Qty Amount
\{desc} $\{price} \{qty} $\{price * qty}
Subtotal $\{price * qty}
Tax $\{price * qty * tax}
Total $\{price * qty * (1.0 + tax)}
""";outputs:
Desc Unit Qty Amount
hammer $7.88 3 $23.64
Subtotal $23.64
Tax $3.546
Total $27.186
Template processor FMT
String form = FMT."""
Desc Unit Qty Amount
%-10s\{desc} $%5.2f\{price} %5d\{qty} $%5.2f\{price * qty}
Subtotal $%5.2f\{price * qty}
Tax $%5.2f\{price * qty * tax}
Total $%5.2f\{price * qty * (1.0 + tax)}
""";outputs:
Desc Unit Qty Amount
hammer $ 7.88 3 $23.64
Subtotal $23.64
Tax $ 3.55
Total $27.19
Custom templating
public interface Processor<RESULT, EX> {
RESULT process(StringTemplate s) throws EX;
}STRis a singleton instanec of aProcessorimplementation.RESULTcan be of any type!
Examples:
// prevents SQL injections
Statement query = SQL."""
SELECT * FROM Person p
WHERE p.\{property} = '\{value}'
""";
// validates & escapes JSON
JSONObject doc = JSON."""
{
"name": "\{name}",
"year": "\{bday.getYear()}"
}
""";Sequence Collections
Collections with order and indexed access:
List
Collections with order without indexed access:
SortedSet(sort order)Deque(insertion order)LinkedHashSet(insertion order)- and more
New interfaces capture the concept of order:
SequencedCollectionSequencedSetSequencedMap
with new API:
void addFirst(E);void addLast(E);E getFirst();E getLast();E removeFirst();E removeLast();SequencedCollection<E> reversed();
Misc improvements
Right-sizing hashing data structures
HashMap.newHashMap(int numMappings)HashSet.newHashSet(int numElements)LinkedHashMap.newLinkedHashMap(int numMappings)LinkedHashSet.newLinkedHashSet(int numElements)
More AutoCloseable
HttpClientExecutorServiceForkJoinPool
Strings and Builders
"String".indexOf(String str, int beginIndex, int endIndex)
DateTimeFormatter
DateTimeFormatter.ofPattern("y-MM-dd")⇒2023-10-02DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)⇒10/2/23using localeen_USDateTimeFormatter.ofLocalizedPattern("yMM")⇒02/10/2023using localeen_US
Better Future
T resultNow()Throwable exceptionNowState state()
Generational ZGC
- optimized for ultra-low pause times
- can have higher memory footprint or higher CPU usage
Virtual Threads
Solve conflit between simplicity and throughput.
There are other conflicts:
- design vs performance (→ Valhalla)
- explicitness vs succinctness (→ Amber)
- flexibility vs safety (→ Panama)
- optimization vs specification (→ Leyden)
What is a Virtual Thread
A virtual thread:
- is a regular
Thread - low memory footprint ([k]bytes)
- small switching cost
- scheduled by the Java runtime
- requires no OS thread when waiting
Virtual memory:
- maps large virtual address space to limited physical memory
- gives illusion of plentiful memory
Virtual threads:
- map large number of virtual threads to a small number of OS threads
- give the illusion of plentiful threads

Sample
try (var executor = Executors
.newVirtualThreadPerTaskExecutor()) {
IntStream
.range(0, 1_000_000)
.forEach(number -> {
executor.submit(() -> {
Thread.sleep(Duration.ofSeconds(1));
return number;
});
});
}Effects
- remove “number of threads” as bottleneck
- match app’s unit of concurrency to Java’s ⇒ simplicity && throughput
- viturla threads increase throughput
Performance
Virtual threads aren’t “faster threads”:
- same number of CPU cycles
- each tasks takes the same time (same latency)
Parallelism VS concurrency
| Parallelism | Concurrency | |
|---|---|---|
| Task origin | solution | problem |
| Control | developer | environment |
| Resource use | coordinated | competitive |
| Metric | latency | throughput |
| Abstraction | CPU cores | tasks |
| # of threads | # of cores | # of tasks |
Example of using Virtual Threads on web frameworks
Spring Boot:
Bean
public TomcatProtocolHandlerCustomizer<?>
createExecutorForSyncCalls() {
return handler -> handler.setExecutor(
Executors.newVirtualThreadPerTaskExecutor());
}
@Bean
public AsyncTaskExecutor
createExecutorForAsyncCalls() {
return new TaskExecutorAdapter(
Executors.newVirtualThreadPerTaskExecutor());
}Data-Oriented Programming
- model the data, the whole data, and nothing but the data
- data is immutable
- validate at the boundary
- make illegal states unrepresentable
Use sealed types to model the alternatives:
- communicates intention to developers
- allow compiler to check exhaustiveness
Pattern matching on sealed types is perfect to apply polymorphic operations to data! And records eschew encapsulation, so everything is accessible.
To keep operations maintainable:
- switch over sealed types
- enumerate all possible types (even if you need to ignore some)
- avoid
defaultbranch
⇒ like the visitor pattern, but less painful
Abstract
Some improvements were implemented in some native Java classes, such as RandomAccess*, so it will improve lots of things, as they are also used by a lot of libraries and frameworks.