Java Programming Tutorials

Java programming tutorials with many code examples!

Java ThreadLocal – thread data separation

One way to prevent concurrent modifications from different Threads is to given them their own local data. Java ThreadLocal allows to do thread data separation.

ThreadLocal holder

Let’s write a simple class that will hold ThreadLocal variable for each thread. We’ll use ThreadLocal.withInitial(Supplier<? extends T>) static factory method to create an instance of ThreadLocal with initialValue throwing exception, because in our case having customer is required:

package com.farenda.java.lang;

import java.util.function.Supplier;

public class CurrentCustomer {

    // No customer is logical error, so we prevent that:
    private static final Supplier<String> STATE_CHECKER = () -> {
        throw new IllegalStateException("Customer must be set!");
    };

    private static final ThreadLocal<String> customer
            = ThreadLocal.withInitial(STATE_CHECKER);

    public static String get() {
        return customer.get();
    }

    public static void set(String id) {
        customer.set(id);
    }
}

Note that access to customer ThreadLocal through get() and set(String id) methods is not synchronized. ThreadLocal takes care of that.

Example Java Application

To test the above code we’ll use ExecutorService to start a few worker threads each of which will process own customer.

Worker accessing ThreadLocal

Simple Worker thread that stores current customer in ThreadLocal variable, does processing (calling a number of layers below that access the same ThreadLocal variable) and shows current customer id at the end:

package com.farenda.java.lang;

import java.util.concurrent.TimeUnit;

public class Worker implements Runnable {

    private final String customer;

    public Worker(String customer) {
        this.customer = customer;
    }

    @Override
    public void run() {
        // Each worker sets own customer:
        CurrentCustomer.set(customer);
        System.out.printf("Thread %s, customer: %s%n",
                Thread.currentThread().getName(),
                CurrentCustomer.get());
        sleep();
        System.out.printf("%s processed customer: %s%n",
                Thread.currentThread().getName(),
                CurrentCustomer.get());
    }

    private void sleep() {
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            // ignore
        }
    }
}

Executing worker threads

package com.farenda.java.lang;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadLocalExample {

    public static void main(String[] args) {
        ExecutorService executor = Executors.newCachedThreadPool();
        executor.execute(new Worker("AAA"));
        executor.execute(new Worker("BBB"));
        executor.execute(new Worker("CCC"));
        executor.shutdown();
    }
}

The output clearly shows that each Worker is accessing the same customer id at the beginning and at the end of execution:

Thread pool-1-thread-3, customer: CCC
Thread pool-1-thread-1, customer: AAA
Thread pool-1-thread-2, customer: BBB
pool-1-thread-1 processed customer: AAA
pool-1-thread-3 processed customer: CCC
pool-1-thread-2 processed customer: BBB
Share with the World!