An Introduction to ThreadLocal in Java

  1. The background is that I have a requirement that needs to modify the saving procedure of an Object. There are different kinds source of saving while we only populate the new filed of the normal save. Say we have create an object and save it, this is allowed. But if we clone it from UI and try to save it, this is not allowed.

  2. So how we tell if it's from cloning?

  3. Ways:

    • Add a new field in database and mark it's source
    • Maintain a flag in memory and check it when saving.
  4. For the second method, I learnt an implementation from others code: ThreadLocal.

  5. What is ThreadLocal

    This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).

    Overall, it's a kind of variable that is thread safe.(each thread has its own copy).

    Variables are set and get only in current thread.

    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).

    Normally, call the remove after you do not use it.

    Since there will be pitfall if you don't do this and you are using ThreadLocal with ThreadPool... Check: An Introduction to ThreadLocal in Java

  6. Why use it?

    For multi thread application, we tends to use extra ways to ensure thread safe.

    ThreadLocal is another way of accomplishing this.(no need of lock)

  7. How to use it

    Use synchornized

    public class ThreadLocalExample {
    
       public static class MyRunnable implements Runnable {
    
            private Object data = null;
    
            @Override
            public void run() {
                synchronized(this)
                {
                    data = (int) (Math.random() * 100D);
                }
    
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                }
    
                synchronized(this) 
                {
                    System.out.println(data);
                }
            }
        }
    
    
        public static void main(String[] args) {
            MyRunnable sharedRunnableInstance = new MyRunnable();
    
            Thread thread1 = new Thread(sharedRunnableInstance);
            Thread thread2 = new Thread(sharedRunnableInstance);
    
            thread1.start();
            thread2.start();
        }
    
    }
    

    Rewrite above code using ThreadLocal

    public class ThreadLocalExample {
       public static class MyRunnable implements Runnable {
     
          private ThreadLocal<Object> threadLocal =
             new ThreadLocal<Object>();
     
          @Override
          public void run() {
             threadLocal.set( (int) (Math.random() * 100D) );
     
             try {
                Thread.sleep(2000); //sleep for 2 secs
             } catch (InterruptedException e) {
             }
     
             System.out.println(threadLocal.get());
          }
       }
     
     
       public static void main(String[] args) {
          MyRunnable sharedRunnableInstance = new MyRunnable();
     
          Thread thread1 = new Thread(sharedRunnableInstance);
          Thread thread2 = new Thread(sharedRunnableInstance);
     
          thread1.start();
          thread2.start();
       }
    }
    

    Performance-wise, **ThreadLocal** offers a better solution than using synchronized blocks because it allows multiple threads to work with the same instance independently.

  8. Reference