Fundamentals 26 min read

From Manual to Autonomous: Java Multithreading Evolution & Virtual Threads

This article chronicles the evolution of Java multithreading from early native threads through the introduction of the concurrency utilities, lock optimizations, the Fork/Join framework, CompletableFuture, reactive streams, and finally virtual threads in JDK 19, explaining each milestone, its motivations, and its impact on modern Java development.

JD Cloud Developers
JD Cloud Developers
JD Cloud Developers
From Manual to Autonomous: Java Multithreading Evolution & Virtual Threads

1. Introduction

On September 22, 2022, JDK 19 was released, highlighting support for virtual threads, adding a new lightweight thread family to the JVM. Virtual threads free the JVM from OS‑scheduled threads, allowing the JVM to schedule threads itself. Early Sun implementations on Solaris experimented with JVM‑scheduled threads but reverted to OS scheduling for simplicity and maintainability.

2. Milestones

A table (illustrated by the following images) maps the evolution of thread‑related concepts in each JDK version, using a car analogy: manual transmission (JDK 1.4 and earlier), automatic transmission (JDK 5‑JDK 18), and autonomous driving (JDK 19+). The analogy emphasizes how JDK 5 introduced a more comfortable way to handle threads, and JDK 19 brings a qualitative performance leap for I/O‑intensive services.

3. Manual Transmission Era

JDK 1.0 Thread and Runnable

JDK 1.0 (January 1996) established Java's basic thread model, which remained largely unchanged. The HotSpot JVM uses pre‑emptive scheduling, and threads were created by extending Thread or implementing Runnable, with start() launching the thread. Coordination relied on the primitive stop, resume, and suspend methods, which could cause deadlocks because suspend does not release locks.

JDK 1.2

The unsafe stop/resume/suspend methods were deprecated in favor of wait/notify/sleep. Atomic classes (e.g., AtomicInteger) were introduced to solve non‑atomic operations like i++. ThreadLocal and the Collections utility class appeared, providing a “turbo‑charged” experience for multithreaded code.

ThreadLocal

ThreadLocal

offers a lock‑free way to give each thread its own instance of a normally non‑thread‑safe object (e.g., SimpleDateFormat, database connections). It avoids a global Map that could leak memory by storing the map inside the thread rather than the ThreadLocal instance.

Collections

The Collections utility introduced thread‑safe wrappers (e.g., SynchronizedList), analogous to a turbo‑charger that boosts performance without changing the underlying data structures.

4. Automatic Transmission Era

JDK 5.0 – Introduction of java.util.concurrent

Doug Lea contributed JSR‑166, leading to the java.util.concurrent (JUC) package, which provides atomic objects, locks, thread pools, and concurrent collections. JUC dramatically lowered the barrier for developers to write correct concurrent code.

Known issues of JUC:

High CPU overhead when CAS spins for a long time.

ABA problem – a value that changes from A→B→A can fool CAS checks; mitigated by version stamps (e.g., AtomicStampedReference).

CAS guarantees atomicity only for a single variable, not for whole code blocks; solutions include using synchronized or AtomicReference for multiple variables.

JDK 6.0 – Synchronized Optimizations

JDK 6 refined lock states from two (unlocked, heavyweight) to four: unlocked, biased, lightweight, and heavyweight, improving acquisition and release efficiency.

JDK 7.0 – Fork/Join Framework

The Fork/Join framework enables recursive task decomposition and result merging. It is heavily used by later features such as CompletableFuture and parallel streams.

JDK 8.0 – CompletableFuture and Stream

CompletableFuture

adds powerful task orchestration capabilities that go beyond simple thread‑pool execution, allowing developers to build complex asynchronous pipelines.

Streams bring functional programming to Java collections, enabling declarative data processing. Parallel stream operations internally use the Fork/Join pool, which can cause contention if misused (see Effective Java’s warning about parallel streams).

Functional programming is a paradigm present in many languages.

Streams implement functional style on collections.

Lambda expressions provide concise anonymous functions.

JDK 9.0 – Contended Locks and Reactive Streams

Lock‑contended mechanisms improve object monitor performance, yielding better throughput in benchmarks. Reactive Streams define a non‑blocking back‑pressure model for asynchronous data flow, with implementations such as Reactor, RxJava, and Akka Streams.

Non‑blocking back‑pressure means producers slow down when consumers cannot keep up, preventing resource exhaustion.

JDK 10 – Thread‑Local Handshakes

Safepoints pause all threads for VM‑wide work (e.g., GC, de‑optimization). Thread‑Local Handshakes allow certain operations (e.g., biased‑lock revocation) to stop only the relevant thread, reducing pause times.

JDK 15 – Deprecation of Biased Locking

Biased locking is removed because modern code rarely benefits from it; many core libraries have moved to lock‑free or fine‑grained synchronization.

5. Autonomous Driving Era

Virtual threads, introduced in JDK 19, provide lightweight, JVM‑managed threads that can be created in large numbers without the overhead of OS threads. They retain the familiar thread‑per‑request programming model, support debugging, and are built on the Fork/Join framework, dramatically improving throughput for I/O‑bound workloads.

References

Java multithreading evolution: https://blog.csdn.net/findmyself_for_world/article/details/41981355

Java 19 Virtual Threads discussion: https://www.zhihu.com/question/536743167

Virtual threads performance article: https://mp.weixin.qq.com/s/yyApBXxpXxVwttr01Hld6Q

VirtualThread source analysis: https://www.cnblogs.com/throwable/p/16758997.html

Linux kernel history: https://blog.csdn.net/weixin_55255438/article/details/126813373

Is the Fork‑Join framework broken?: https://softwareengineering.stackexchange.com/questions/343402/is-the-fork-join-framework-in-java-broken

Java Concurrency Evolution: https://dzone.com/articles/java-concurrency-evolution

Spring 5 functional programming: https://www.zhihu.com/question/52567283

Java lock contention in JDK 9: https://blog.csdn.net/weixin_42509766/article/details/114610986

Contended locks explained: https://ionutbalosin.com/2018/06/contended-locks-explained-a-performance-approach/

Thread‑Local Handshakes in JDK 10: https://cloud.tencent.com/developer/article/1115657

Biased‑locking deprecation: https://www.reddit.com/r/java/comments/dy324a/disable_biasedlocking_and_deprecate_all_flags/

Why Do We Need CompletableFuture?: https://mincong.io/2020/06/26/completable-future/

JUC details: https://www.lsjlt.com/news/140687.html

Reactive Streams overview: https://blog.csdn.net/wudaoshihun/article/details/83070086

Java 19 virtual threads deep dive: https://blog.csdn.net/qq_25353539/article/details/126739697

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaJDKmultithreadingVirtual ThreadsJava 19
JD Cloud Developers
Written by

JD Cloud Developers

JD Cloud Developers (Developer of JD Technology) is a JD Technology Group platform offering technical sharing and communication for AI, cloud computing, IoT and related developers. It publishes JD product technical information, industry content, and tech event news. Embrace technology and partner with developers to envision the future.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.