
j.l.Thread, j.u.concurrentgoto with threadsawaitTermination
wait, notify, synchronizedjava.util.concurrent
ReentrantLock, ConcurrentHashMap, Executor, Future

AsyncTask/SwingWorker

Thread thread = Thread.startVirtualThread(runnable); // Already started!!!
Thread thread = Thread.builder() .virtual() .name(taskname) .task(runnable) .build();

ExecutorService exec = Executors.newVirtualThreadExecutor(); exec.submit(runnable1); Future<T> result = exec.submit(callable2);
ThreadFactory myfactory = Thread.builder()
.virtual()
.name("myfactory-", 1)
.factory();
ExecutorService exec = Executors.newThreadExecutor(myfactory);
exec = Executors.newCachedThreadPool(myfactory); // Bad idea

final int NTASKS = 1_000_000;
ExecutorService exec = Executors.newVirtualThreadExecutor();
for (int i = 1; i <= NTASKS; i++) {
exec.submit(() -> run(i));
}
Thread.sleep makes the current fiber sleep:
public static Void run(Object obj) {
Thread.sleep((int) (DELAY * (0.5 + Math.random())));
System.out.println(obj);
return null; // Pro tip—turns lambda into Callable
}

Thread.sleepj.u.c LocksReentrantLock is ok)goto
go myfunc(); new Thread(this::myfunc).start();
goto with branches, loops, functions
exec.close() blocks until all tasks are done
Executor is autocloseable:
try (ExecutorService exec = Executor.newVirtualThreadExecutor()) {
for (int i = 0; i < NTASKS; i++) {
exec.schedule(() -> run(i));
}
} // Blocks until all threads completed
invokeAny, invokeAllExecutorService exec = Executors.newVirtualThreadExecutor().withDeadline( Instant.now().plus(30, ChronoUnit.SECONDS))

try {
. . .
while(!done) {
if (Thread.interrupted()) throw new InterruptedException();
. . .
}
} catch (InterruptedException ex) {
. . . // Clean up
}
ExecutorService.invokeAny cancels remaining tasksCompletableFuture.anyOf doesn'tExecutorService.shutdownNow, expired deadline interrupts remaining tasks
public static ThreadLocal<Connection> connTL = ThreadLocal<>.withInitial(() -> dbManager.openConnection()); // Much later, several function calls deep Connection conn = connTL.get() // Gets the value for the current thread
SimpleDateFormat
Thread.Builder methods noThreadLocals(), noInheritableThreadLocals()LightweightThreadLocal<Connection> connTL = ThreadLocal.forType(Connection.class);
. . .
try (var connection = dbManager.openConnection();
var __ = connTL.bind(connection);
var exec = Executors.newVirtualThreadExecutor()) {
exec.submitTasks(tasks); // The tasks can read connTL
}
try (var __ = connTL.bind(connection)) {
userList.parallelStream().forEach(u -> persistUser(u));
}

https://dilbert.com/strip/2011-06-05 CompletableFuture .completedFuture(getUrlForDate(date)) .thenComposeAsync(this::readPage, executor) .thenApply(this::getImageUrl) .thenComposeAsync(this::readPage) .thenAccept(this::process);

try (ExecutorService exec = Executors.newVirtualThreadExecutor()) {
LocalDate date = LocalDate.now();
for (int i = 0; i < NUMBER_TO_SHOW; i++) {
ImageInfo info = new WikimediaImageInfo(date);
exec.submit(() -> load(info));
date = date.minusDays(1);
}
}load

Thread.start, Thread.sleepThread.interrupted, InterruptedExceptionThread.joinsynchronized methods, synchronized blocksvolatilewait, notifyAll
Runnable is a task (presumably with a side effect)Callable<T> delivers a result (hopefully without side effects)Runnable task = () -> { ... };
ExecutorService exec = ...;
exec.execute(task);
Callable<Long> task = () -> { ...; return count; }
Future<Long> result = exec.submit(task);
result.get() blocks.
ExecutorService.invokeAll/invokeAny for composing resultsCompletableFuture)
synchronized, notifyAll, volatile
j.u.concurrent has ConcurrentHashMap, LinkedBlockingQueue, AtomicLongSemaphore, CountDownLatch, CyclicBarrier, Phaser?
awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS)
j.u.stream