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.
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
ThreadLocaloffers 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
CompletableFutureadds 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
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
