ThreadLocal in Java



Java Multithreading ThreadLocal

ThreadLocal in Java

ThreadLocal is a Java concurrency technique where each thread has its own variables. In other words, if multiple threads read the same code, they cannot read and write on each other thread’s local variables. This means that each thread can read and write only its own variables. This is the simplest way of achieving thread safety instead of creating immutable classes. The Java ThreadLocal variables are always private and static whose scope is within the thread.

ThreadLocal in Java

Constructors of ThreadLocal

ThreadLocal has only a single constructor that creates an empty variable in Java.

ThreadLocal tl = new ThreadLocal();

Methods of ThreadLocal

Below are the methods of ThreadLocal in Java.

MethodDescription
Integer get()Returns the value of the current thread's local variable
void remove()Removes the value of the current thread's local variable
void set(Integer value)Sets the specified value to the current thread's local variable
ThreadLocal withInitial(Supplier supplier)Creates a new thread local variable

Benefits of ThreadLocal

  • Multithreading is easier since it does not share its state across objects.
  • Achieves thread-safety
  • Does not require synchronization

Disadvantages of ThreadLocal

  • Hides coupling among classes
  • Abuses the ThreadLocal due to its visibility restriction

Example: Create, set, and get ThreadLocal

In this example, we will see how to create, set, and retrieve the value of the ThreadLocal variable. First, we create a ThreadLocal variable of Integer type. Inside the run() method, we increment the variable value and set it using the set() method. We can retrieve the value using the get() method. Using the Thread class, we create 2 threads and invoke the run() method using start().

public class ThreadLocalDemo {

  public static void main(String[] args) {
    ThreadDemo td = new ThreadDemo();
    
    Thread t1 = new Thread(td);
    Thread t2 = new Thread(td);
    
    t1.start();
    t2.start();
    
  }

}

class ThreadDemo implements Runnable {
  
  private ThreadLocal<Integer> tl = new ThreadLocal<Integer>();
  private int num = 10;

  @Override
  public void run() {
    
    tl.set(num++);
    try {
      Thread.sleep(1000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("ThreadLocal variable value: " + tl.get());
    
  }
  
}
ThreadLocal variable value: 11
ThreadLocal variable value: 10

Example: remove() method

The remove() method deletes the value of the ThreadLocal variable for the corresponding thread.

public class ThreadLocalDemo {

  public static void main(String[] args) {
    ThreadDemo td = new ThreadDemo();
    
    Thread t1 = new Thread(td);
    Thread t2 = new Thread(td);
    
    t1.start();
    t2.start();
    
  }

}

class ThreadDemo implements Runnable {
  
  private ThreadLocal<Integer> tl = new ThreadLocal<Integer>();
  private int num = 10;

  @Override
  public void run() {
    
    tl.set(num++);
    try {
      Thread.sleep(1000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("ThreadLocal variable value: " + tl.get());
    tl.remove();
    System.out.println("ThreadLocal variable value: " + tl.get());
    
  }
  
}
ThreadLocal variable value: 10
ThreadLocal variable value: 11
ThreadLocal variable value: null
ThreadLocal variable value: null

Example: initialValue() method

The initialValue() method returns the initial value of the java ThreadLocal variable. In this example, we initialize and increment the local variable value by overriding the initialValue() method. Hence each time we call the get() method, it returns the incremented value of the local variable. In this way, we can initialize the values before using the set() method.

public class ThreadInitialValueDemo {

  public static void main(String[] args) {
    ThreadEx t1 = new ThreadEx("Thread 1");
    ThreadEx t2 = new ThreadEx("Thread 2");
  }
  
  
}
class ThreadEx extends Thread {
  
  private static int num = 20;
  private static ThreadLocal t = new ThreadLocal() {
    protected Object initialValue() {
      return new Integer(num ++);
    }
  };
  
  public void run() {
    System.out.println(t.get());
  }
  
  ThreadEx(String name){
    super(name);
    start();
  }
  
}
20
21

Supplier Implementation

There is another method of initializing the value of the ThreadLocal variable in Java by using the Supplier implementation. The Supplier interface has a static factory method which is withInitial(). By using this, we can supply initial values to the local variable. It is always recommendable to use the lambda expression since Supplier is a functional interface.

The below example displays the current milliseconds time initialized for the local variable.

public class SupplierInitialValue {

  public static void main(String[] args) {
    Demo d1 = new Demo();
    Demo d2 = new Demo();
    d1.start();
    d2.start();
  }
}

class Demo extends Thread {
  ThreadLocal<String> tl = ThreadLocal.withInitial(() -> String.valueOf(System.currentTimeMillis()));
  
  public void run() {
    System.out.println(tl.get());
    
    try {
      Thread.sleep(2000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }	    
}
1614477848700
1614477848699

Generic ThreadLocal

Generic ThreadLocal helps to create variables of that specified type. For example, in the below code, the variable accepts only data of String type.

ThreadLocal<String> tl = new ThreadLocal<String>();
tl.set("Welcome");
String text = tl.get();

Conclusion

In this tutorial, we have discussed the ThreadLocal variable in Java which is a concurrency technique used in a multithreading environment.

Reference