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

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

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

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

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • How to Merge HTML Documents in Java
  • The Future of Java and AI: Coding in 2025
  • Tired of Spring Overhead? Try Dropwizard for Your Next Java Microservice
  • Using Python Libraries in Java

Trending

  • How to Merge HTML Documents in Java
  • Creating a Web Project: Caching for Performance Optimization
  • Detection and Mitigation of Lateral Movement in Cloud Networks
  • Exploring Intercooler.js: Simplify AJAX With HTML Attributes
  1. DZone
  2. Coding
  3. Java
  4. A Painless Introduction to Java's ThreadLocal Storage

A Painless Introduction to Java's ThreadLocal Storage

Let’s look at some best practices for using another powerful class: ThreadLocal from java.lang, which is also implemented using WeakReference.

By 
Patson Luk user avatar
Patson Luk
·
Sep. 16, 13 · Tutorial
Likes (3)
Comment
Save
Tweet
Share
134.0K Views

Join the DZone community and get the full member experience.

Join For Free

In my last blog post we explored WeakHashMap, which is based on WeakReference in Java. Now let’s look at some best practices for using another powerful class: ThreadLocal from java.lang, which is also implemented using WeakReference.

What is ThreadLocal? A Simple Example

As its name suggests, a single instance of ThreadLocal can store different values for each thread independently. Therefore, the value stored in a ThreadLocal instance is specific (local) to the current running thread and any other code logic running on the same thread will see the same value, but not the values set on the same instance by other threads. (There are exceptions, like InheritableThreadLocal, which inherits its parent thread’s values by default.)

Let’s consider this example:

We have a TransactionManager class that provides static methods to:

  • Start a transaction with a generated ID
  • Store that ID as a static field and provide a transaction-ID-getter method to other code logic that needs to know the current transaction ID

In a single-threaded environment, TransactionManager can simply store the ID as a static field and return it as is. However, this will certainly not work in a multiple-threaded environment. Imagine if multiple threads used TransactionManager. Transaction IDs generated by each thread could overwrite each other since there is only one static instance of transaction ID. One may synchronize and block other transactions to avoid overwrites, but this would totally defeat the purpose of having multiple threads.

In order to solve this problem, ThreadLocal provides a very neat solution:

public class TransactionManager {
private static final ThreadLocal<String> context = new ThreadLocal<String>();
public static void startTransaction() {
//logic to start a transaction
//...
context.set(generatedId); 
}
public static String getTransactionId() {
return context.get();
}
public static void endTransaction() {
//logic to end a transaction
//…
context.remove();
}
}

A different thread that starts transactions via TransactionManager will get its own transaction ID stored in the context. Any logic within the same thread can call getTransactionId() later on to retrieve the value that belongs to or is local to that thread. So, problem solved!

The Internals of ThreadLocal and How it Works

Let’s drill down a little bit into ThreadLocal’s internals. ThreadLocal is implemented by having a map (a ThreadLocalMap) as a field (with a WeakReference entry) within each thread instance. (There are actually two maps. The second one is used for InheritableThreadLocal, but let’s not complicate things.) The keys of those maps are the corresponding ThreadLocals themselves. Therefore, when a set/get is called on a ThreadLocal, it looks at the current thread, finds the map, and looks up the value with “this” ThreadLocal instance.

Still confused? I certainly am. Let’s look at a real example.

  • Code running in Thread 1 calls set() on ThreadLocal instance “A” with value "123"
  • Code running in Thread 2 calls set() on ThreadLocal instance “A” with value "234"
  • Code running in Thread 1 calls set() on ThreadLocal instance “B” with value "345"

And this is the end result:

Thread 1 (the instance)'s field ThreadLocalMap (m1) has two entries:

KeyValue
ThreadLocal A"123"
ThreadLocal B"345"

Thread 2 (the instance)’s field ThreadLocalMap (m2) has one entry:

KeyValue
ThreadLocal A"234"

Now if some code logic in Thread 1 calls get() on ThreadLocal instance “A”, the ThreadLocal logic will look up the current Thread, which is the instance Thread 1, then access the field ThreadLocalMap of that Thread instance, which is m1, it can then look up the value by using m1.get(this), with “this” as ThreadLocal and the result will be “123″.

Know What to Watch Out For!

Did I hear weak references for ThreadLocal entries? Does that mean I don’t have to clean up? Well, it’s not quite that simple.

First of all, the value object put into the ThreadLocal would not purge itself (garbage collected) if there are no more strong references to it. Instead, the weak reference is done on the thread instance, which means Java garbage collection would clean up the ThreadLocal map if the thread itself is not strongly referenced elsewhere.

So now the question is: when would the thread object get garbage collected?

The answer is: it depends, but always assume the thread is long-running. Two common examples:

  • Servlets: The threads that handle servlet requests usually stay alive in the container for the lifetime of the server instance. Code logic that uses ThreadLocal might be referenced indirectly by servlets.
  • Thread pooling java.util.concurrent.Executors: Java encourages recycling threads!

A typical usage of Executor introduced in Java 1.5 is that, if ThreadLocal maps are not cleaned up properly after a transaction is done, the next TransactionProcessingTask might inherit values from another previous, unrelated task!

ExecutorService service = Executors.newFixedThreadPool(10);
service.submit(new TransactionProcessingTask());

Be careful with initialization of ThreadLocal. Below is an implementation of a counter by thread. Can you tell what is wrong in the initialization below?

public class Counter {
private static ThreadLocal<Integer> counter = new ThreadLocal<Integer>();
static {
counter.set(0);
}
public int getCountInThread() {
return counter.get();
}
//….
}

The counter would not get initialized correctly! Though the counter is declared as static, it CANNOT be initialized by having a static initializer, since the initializer only runs once when the first thread references the Counter class. When the second thread comes in, it does not run counter.set(0) on that thread, therefore counter.get() returns null instead of 0!

With this in mind, you can probably picture the consequences of not cleaning up after yourself! An operation that runs on a recycled thread might inherit the values from a previous operation on the same thread! Besides, it can also cause memory leaks, since the instance stored in ThreadLocal will never get garbage collected if the thread is alive.

As a rule of thumb, always clean up or reset your ThreadLocal after you have finished your unit of operation! Even though the current code might be simple enough to bypass the clean-ups, it might be adapted and integrated into servlets/thread pooling later on! After all, cleaning up responsibly is always appreciated both in the realms of programming and real life.


Java (programming language)

Opinions expressed by DZone contributors are their own.

Related

  • How to Merge HTML Documents in Java
  • The Future of Java and AI: Coding in 2025
  • Tired of Spring Overhead? Try Dropwizard for Your Next Java Microservice
  • Using Python Libraries in Java

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!