DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • Quarkus 3: The Future of Java Microservices With Virtual Threads and Beyond
  • Micro Frontends for Quarkus Microservices
  • Microservices With Apache Camel and Quarkus (Part 4)
  • Microservices With Apache Camel and Quarkus (Part 3)

Trending

  • Apache Spark 4.0: Transforming Big Data Analytics to the Next Level
  • Caching 101: Theory, Algorithms, Tools, and Best Practices
  • How to Format Articles for DZone
  • After 9 Years, Microsoft Fulfills This Windows Feature Request
  1. DZone
  2. Coding
  3. Java
  4. Taming the Virtual Threads: Embracing Concurrency With Pitfall Avoidance

Taming the Virtual Threads: Embracing Concurrency With Pitfall Avoidance

Learn what challenges you probably face when you try to run your microservices on the virtual threads. The challenge might be more complex and bigger if you need to import more Java libraries.

By 
Daniel Oh user avatar
Daniel Oh
DZone Core CORE ·
Dec. 03, 23 · Tutorial
Likes (4)
Comment
Save
Tweet
Share
9.7K Views

Join the DZone community and get the full member experience.

Join For Free

In the previous article, you learned about the history of Java threads and how Quarkus helps developers execute traditional blocking applications on top of the virtual thread (Project Loom) dead simply using a single @RunOnVirtualThread annotation on both method and class levels. 

Unfortunately, there’s a long way to go with the virtual threads across all Java ecosystems. For example, you probably get used to importing Java libraries such as Maven dependencies or Gradle modules in your Java projects. It means you might not know how the libraries work on platform threads, or you don’t even need to know it because you just need to use them, which is the common development practice. With a vast ecosystem of Java libraries, you will probably hit the following challenges and pitfalls that could break the virtual thread runtimes in the end.

Pinning

In Java virtual threads, pinning refers to the condition where a virtual thread is stuck to its carrier thread that maps the platform thread. It means that the virtual thread can’t be unmounted from the carrier threads because the state of the virtual thread can’t be stored in the heap memory for several reasons. 

For example, when a virtual thread enters a synchronized block or method, it becomes pinned to its carrier thread because synchronized blocks and methods require exclusive access to a shared resource, and only one thread can enter a synchronized block or method at a time, as shown in Figure 1. 

Figure 1: Pinning carrier thread

When you perform a blocking operation on a virtual thread, such as waiting for input or output, it also becomes pinned to its carrier thread because the virtual thread cannot continue executing until the blocking operation completes. The pinning also happens when you make JNI calls executed by native code because the native code can only run on one thread at a time.

Here is an example of the pinning code:

Java
 
Object monitor = new Object();

//...

public void makePinTheCarrierThread() throws Exception {

  synchronized(monitor) {

    Thread.sleep(1000); 

  }

}

While virtual threads offer enhanced concurrency and performance potential, many Java libraries currently cause carrier thread pinning, potentially hindering application performance. Quarkus has proactively addressed this issue by patching several libraries, such as Narayana and Hibernate ORM, to eliminate pinning. However, as developers adopt virtual threads, it remains crucial to exercise caution when utilizing third-party libraries, as not all code has been optimized for virtual thread compatibility. A gradual transition towards virtual thread-friendly code is underway, and the benefits will continue to accrue as the ecosystem adapts.

Monopolization

When a virtual thread needs to process a long computation, the virtual thread excessively occupies its carrier thread, preventing other virtual threads from utilizing that carrier thread. For example, when a virtual thread repeatedly performs blocking operations, such as waiting for input or output, it monopolizes the carrier thread, preventing other virtual threads from making progress. Inefficient resource management within the virtual thread can also lead to excessive resource utilization, causing monopolization of the carrier thread.

Monopolization can have detrimental effects on the performance and scalability of virtual thread applications. It can lead to increased contention for carrier threads, reduced overall concurrency, and potential deadlocks. To mitigate these issues, developers should strive to minimize monopolization by breaking down lengthy computations into smaller, more manageable tasks to allow other virtual threads to interleave and utilize the carrier thread.

Object Pooling

To mitigate consuming expensive platform threads, object pooling was invented to manage and reuse frequently used objects to optimize performance and reduce memory overhead. Object pooling also involves maintaining a pool of pre-created objects that are available for use by platform threads. For example, Threadlocal is one of the good examples to implement object pooling. However, a virtual thread requires a new object by creating a new instance rather than retrieving it from the object pool. In addition, when the virtual thread is finished, the object is deleted rather than returning it to the pool for later reuse.

When you use Java libraries that utilize these pooling patterns, your application will reduce the performance of the virtual threads because you will see many allocations of large objects, as every virtual thread will get its instance of the object. It should be an easy job to rewrite the libraries to avoid the object pooling pattern. Quarkus also uses Jackson to store expensive objects in the thread locals. Luckily, there’s already PR to address this problem, which makes the Quarkus application more virtual thread-friendly.

Conclusion

You learned what challenges you probably face when you try to run your microservices on virtual threads. The challenge might be more complex and bigger if you need to import more Java libraries to implement a variety of business domains, such as REST client, messaging, gRPC, event bus, and more. This tells us that there’s a long way to go for running all business services with virtual threads. 

In the meantime, Quarkus has already patched and rewritten existing Quarkus extensions such as Apache Kafka, AMQP, Apache Pulsar, JMS, REST client, Redis, Mailer, event bus, scheduled methods, gRPC, and more for virtual threads. In the next article, I’ll introduce an insightful performance comparison of how virtual threads can be better or worse compared to blocking and reactive applications.

Apache Maven Java Native Interface Quarkus Java (programming language) microservice

Opinions expressed by DZone contributors are their own.

Related

  • Quarkus 3: The Future of Java Microservices With Virtual Threads and Beyond
  • Micro Frontends for Quarkus Microservices
  • Microservices With Apache Camel and Quarkus (Part 4)
  • Microservices With Apache Camel and Quarkus (Part 3)

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!