April 23, 2024

ThreadLocal Class in Java With Examples

In a multi-threaded environment a shared object would need synchronization to avoid corruption through concurrent access, but synchronization is expensive. Another alternative is to give each thread its own instance and avoid sharing of data. That’s what ThreadLocal class in Java does.

ThreadLocal class in Java provides thread local variables where each thread has its own, independently initialized copy of the variable.

How to create and access thread-local variables

Using ThreadLocal() constructor you can create a thread local variable. For example if you want to create a thread local variable that stores an integer value for individual threads.

private static final ThreadLocal<Integer> tcValue = new ThreadLocal<Integer>();

Here note that ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread.

To get or set value to this thread local variable you can use get() and set() methods of the ThreadLocal class.

tcValue.set(1);
Integer threadId = tcValue.get();

You can use initialValue() method to return the current thread's "initial value" for this thread-local variable. This method will be invoked the first time a thread accesses the variable with the get() method. The default implementation of initialValue() in the ThreadLocal class just returns null.

If you want your thread-local variables to have an initial value other than null you need to subclass ThreadLocal and override initialValue() method.

Java 8 onward withInitial(Supplier<? extends S> supplier) method can also be used to create a thread local variable. Since this method uses Supplier functional interface as parameter so lambda expression can be used to implement it.

Here is a code snippet that puts these methods to use to make it clearer.

private static final AtomicInteger nextId = new AtomicInteger(0);

// Thread local variable with initialValue() implementation to 
//return initial value to each thread
private static final ThreadLocal threadId =
  new ThreadLocal() {
    @Override 
    protected Integer initialValue() {
      return nextId.getAndIncrement();
    }
  };

If you are using withInitial() method then you can replace initialValue() implementation with the following code.

private static final ThreadLocal<Integer> threadId  = 
     ThreadLocal.withInitial(()-> {return nextId.getAndIncrement();});

Java ThreadLocal class example

1- One use of ThreadLocal class is in the scenario where you want to associate state with each thread (user ID or Transaction ID). In that case you can assign thread-local variable to each thread with a unique value. Just to verify assigned IDs are displayed again in another method.

import java.util.concurrent.atomic.AtomicInteger;

class UniqueIdGenerator{
  private static final AtomicInteger nextId = new AtomicInteger(0);
  // ThreadLocal variable
  private static final ThreadLocal<Integer> threadId = new ThreadLocal<Integer>() {
    @Override 
    protected Integer initialValue() {
     return nextId.getAndIncrement();
    }
  };
  // Returns the current thread's unique ID, assigning it if necessary
  public static int getId() {
    return threadId.get();
  }
}
public class ThreadClassDemo implements Runnable{
  @Override
  public void run() {
    System.out.println("Thread " + Thread.currentThread().getName() 
        + " Value - " +  UniqueIdGenerator.getId());
    ThreadClassDemo td = new ThreadClassDemo();
    // display stored Id again to verify
    td.displayThreadId();
  }
	
  public void displayThreadId(){
    System.out.println("Thread " + Thread.currentThread().getName() 
          + " Stored Value - " +  UniqueIdGenerator.getId());
  }
  public static void main(String[] args) {
    //ThreadClassDemo td = new ThreadClassDemo();
    Thread thread1 = new Thread(new ThreadClassDemo());
    Thread thread2 = new Thread(new ThreadClassDemo());
    Thread thread3 = new Thread(new ThreadClassDemo());
    Thread thread4 = new Thread(new ThreadClassDemo());
    Thread thread5 = new Thread(new ThreadClassDemo());
    thread1.start();
    thread2.start();
    thread3.start();
    thread4.start();
    thread5.start();
  }
}
Output
Thread Thread-3 Value - 2
Thread Thread-0 Value - 0
Thread Thread-2 Value - 4
Thread Thread-4 Value - 3
Thread Thread-1 Value - 1
Thread Thread-1 Stored Value - 1
Thread Thread-4 Stored Value - 3
Thread Thread-2 Stored Value - 4
Thread Thread-0 Stored Value - 0
Thread Thread-3 Stored Value – 2

2- You can also use ThreadLocal as an alternative to synchronizing the code as synchronization is costly. When you use SimpleDateFormat in a multi-threaded environment you do need to synchronize it as instance of SimpleDateFormat is not thread safe. Using ThreadLocal you can construct instance of SimpleDateFormat per thread. Since each thread will have its own instance local to that thread so no chance of interference by another thread.

import java.text.SimpleDateFormat;
import java.util.Date;

class DateFormatInstance{
  // ThreadLocal variable
  private static final ThreadLocal<SimpleDateFormat> threadLocalDateFmt = 
      ThreadLocal.withInitial(()-> {return new SimpleDateFormat("dd/MM/yyyy");});

  public static SimpleDateFormat getFormat() {
    return threadLocalDateFmt.get();
  }
}
public class ThreadClassDemo implements Runnable{
  @Override
  public void run() {
    System.out.println(Thread.currentThread().getName() + " Date formatter pattern is - " 
      + DateFormatInstance.getFormat().toPattern());
    System.out.println("Formatted date - " 
      + DateFormatInstance.getFormat().format(new Date()));
  }
	
  public static void main(String[] args) {
    //ThreadClassDemo td = new ThreadClassDemo();
    Thread thread1 = new Thread(new ThreadClassDemo());
    Thread thread2 = new Thread(new ThreadClassDemo());
    Thread thread3 = new Thread(new ThreadClassDemo());

    thread1.start();
    thread2.start();
    thread3.start();
  }
}
Output
Thread-1 Date formatter pattern is - dd/MM/yyyy
Thread-2 Date formatter pattern is - dd/MM/yyyy
Thread-0 Date formatter pattern is - dd/MM/yyyy
Formatted date - 10/05/2018
Formatted date - 10/05/2018
Formatted date - 10/05/2018

Important points about ThreadLocal in Java

  1. Thread-local variable is local to a thread. Each thread has its own, independently initialized copy of the variable.
  2. Each thread has global access to its own thread-local variable. If a thread is calling several methods, thread-local variable can be accessed in all of those methods.
  3. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread
  4. Each thread holds an implicit reference to its copy of a thread-local variable as long as the thread is alive and the ThreadLocal instance is accessible; after a thread goes away, all of its copies of thread-local instances are subject to garbage collection (unless other references to these copies exist).

Reference:https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/lang/ThreadLocal.html

That's all for the topic ThreadLocal Class in Java With Examples. If something is missing or you have something to share about the topic please write a comment.


You may also like

No comments:

Post a Comment