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;
}
STR
is a singleton instanec of aProcessor
implementation.RESULT
can 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:
SequencedCollection
SequencedSet
SequencedMap
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
HttpClient
ExecutorService
ForkJoinPool
Strings and Builders
"String".indexOf(String str, int beginIndex, int endIndex)
DateTimeFormatter
DateTimeFormatter.ofPattern("y-MM-dd")
⇒2023-10-02
DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)
⇒10/2/23
using localeen_US
DateTimeFormatter.ofLocalizedPattern("yMM")
⇒02/10/2023
using localeen_US
Better Future
T resultNow()
Throwable exceptionNow
State 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
default
branch
⇒ 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.