When and how should I use a ThreadLocal variable?

前端 未结 25 1944
再見小時候
再見小時候 2020-11-22 12:35

When should I use a ThreadLocal variable?

How is it used?

相关标签:
25条回答
  • 2020-11-22 12:54

    The documentation says it very well: "each thread that accesses [a thread-local variable] (via its get or set method) has its own, independently initialized copy of the variable".

    You use one when each thread must have its own copy of something. By default, data is shared between threads.

    0 讨论(0)
  • 2020-11-22 12:55

    Essentially, when you need a variable's value to depend on the current thread and it isn't convenient for you to attach the value to the thread in some other way (for example, subclassing thread).

    A typical case is where some other framework has created the thread that your code is running in, e.g. a servlet container, or where it just makes more sense to use ThreadLocal because your variable is then "in its logical place" (rather than a variable hanging from a Thread subclass or in some other hash map).

    On my web site, I have some further discussion and examples of when to use ThreadLocal that may also be of interest.

    Some people advocate using ThreadLocal as a way to attach a "thread ID" to each thread in certain concurrent algorithms where you need a thread number (see e.g. Herlihy & Shavit). In such cases, check that you're really getting a benefit!

    0 讨论(0)
  • 2020-11-22 12:55

    You have to be very careful with the ThreadLocal pattern. There are some major down sides like Phil mentioned, but one that wasn't mentioned is to make sure that the code that sets up the ThreadLocal context isn't "re-entrant."

    Bad things can happen when the code that sets the information gets run a second or third time because information on your thread can start to mutate when you didn't expect it. So take care to make sure the ThreadLocal information hasn't been set before you set it again.

    0 讨论(0)
  • 2020-11-22 12:56

    when?

    When an object is not thread-safe, instead of synchronization which hampers the scalability, give one object to every thread and keep it thread scope, which is ThreadLocal. One of most often used but not thread-safe objects are database Connection and JMSConnection.

    How ?

    One example is Spring framework uses ThreadLocal heavily for managing transactions behind the scenes by keeping these connection objects in ThreadLocal variables. At high level, when a transaction is started it gets the connection ( and disables the auto commit ) and keeps it in ThreadLocal. on further db calls it uses same connection to communicate with db. At the end, it takes the connection from ThreadLocal and commits ( or rollback ) the transaction and releases the connection.

    I think log4j also uses ThreadLocal for maintaining MDC.

    0 讨论(0)
  • 2020-11-22 12:57

    ThreadLocal will ensure accessing the mutable object by the multiple threads in the non synchronized method is synchronized, means making the mutable object to be immutable within the method.

    This is achieved by giving new instance of mutable object for each thread try accessing it. So It is local copy to the each thread. This is some hack on making instance variable in a method to be accessed like a local variable. As you aware method local variable is only available to the thread, one difference is; method local variables will not available to the thread once method execution is over where as mutable object shared with threadlocal will be available across multiple methods till we clean it up.

    By Definition:

    The ThreadLocal class in Java enables you to create variables that can only be read and written by the same thread. Thus, even if two threads are executing the same code, and the code has a reference to a ThreadLocal variable, then the two threads cannot see each other's ThreadLocal variables.

    Each Thread in java contains ThreadLocalMap in it.
    Where

    Key = One ThreadLocal object shared across threads.
    value = Mutable object which has to be used synchronously, this will be instantiated for each thread.
    

    Achieving the ThreadLocal:

    Now create a wrapper class for ThreadLocal which is going to hold the mutable object like below (with or without initialValue()).
    Now getter and setter of this wrapper will work on threadlocal instance instead of mutable object.

    If getter() of threadlocal didn't find any value with in the threadlocalmap of the Thread; then it will invoke the initialValue() to get its private copy with respect to the thread.

    class SimpleDateFormatInstancePerThread {
    
        private static final ThreadLocal<SimpleDateFormat> dateFormatHolder = new ThreadLocal<SimpleDateFormat>() {
    
            @Override
            protected SimpleDateFormat initialValue() {
                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd") {
                    UUID id = UUID.randomUUID();
                    @Override
                    public String toString() {
                        return id.toString();
                    };
                };
                System.out.println("Creating SimpleDateFormat instance " + dateFormat +" for Thread : " + Thread.currentThread().getName());
                return dateFormat;
            }
        };
    
        /*
         * Every time there is a call for DateFormat, ThreadLocal will return calling
         * Thread's copy of SimpleDateFormat
         */
        public static DateFormat getDateFormatter() {
            return dateFormatHolder.get();
        }
    
        public static void cleanup() {
            dateFormatHolder.remove();
        }
    }
    

    Now wrapper.getDateFormatter() will call threadlocal.get() and that will check the currentThread.threadLocalMap contains this (threadlocal) instance.
    If yes return the value (SimpleDateFormat) for corresponding threadlocal instance
    else add the map with this threadlocal instance, initialValue().

    Herewith thread safety achieved on this mutable class; by each thread is working with its own mutable instance but with same ThreadLocal instance. Means All the thread will share the same ThreadLocal instance as key, but different SimpleDateFormat instance as value.

    https://github.com/skanagavelu/yt.tech/blob/master/src/ThreadLocalTest.java

    0 讨论(0)
  • 2020-11-22 12:59

    Two use cases where threadlocal variable can be used -
    1- When we have a requirement to associate state with a thread (e.g., a user ID or Transaction ID). That usually happens with a web application that every request going to a servlet has a unique transactionID associated with it.

    // This class will provide a thread local variable which
    // will provide a unique ID for each thread
    class ThreadId {
        // Atomic integer containing the next thread ID to be assigned
        private static final AtomicInteger nextId = new AtomicInteger(0);
    
        // Thread local variable containing each thread's ID
        private static final ThreadLocal<Integer> threadId =
            ThreadLocal.<Integer>withInitial(()-> {return nextId.getAndIncrement();});
    
        // Returns the current thread's unique ID, assigning it if necessary
        public static int get() {
            return threadId.get();
        }
    }
    

    Note that here the method withInitial is implemented using lambda expression.
    2- Another use case is when we want to have a thread safe instance and we don't want to use synchronization as the performance cost with synchronization is more. One such case is when SimpleDateFormat is used. Since SimpleDateFormat is not thread safe so we have to provide mechanism to make it thread safe.

    public class ThreadLocalDemo1 implements Runnable {
        // threadlocal variable is created
        private static final ThreadLocal<SimpleDateFormat> dateFormat = new ThreadLocal<SimpleDateFormat>(){
            @Override
            protected SimpleDateFormat initialValue(){
                System.out.println("Initializing SimpleDateFormat for - " + Thread.currentThread().getName() );
                return new SimpleDateFormat("dd/MM/yyyy");
            }
        };
    
        public static void main(String[] args) {
            ThreadLocalDemo1 td = new ThreadLocalDemo1();
            // Two threads are created
            Thread t1 = new Thread(td, "Thread-1");
            Thread t2 = new Thread(td, "Thread-2");
            t1.start();
            t2.start();
        }
    
        @Override
        public void run() {
            System.out.println("Thread run execution started for " + Thread.currentThread().getName());
            System.out.println("Date formatter pattern is  " + dateFormat.get().toPattern());
            System.out.println("Formatted date is " + dateFormat.get().format(new Date()));
        } 
    
    }
    
    0 讨论(0)
提交回复
热议问题