How to initialize ThreadLocal objects in Java
Asked Answered
L

4

33

I'm having an issue where I'm creating a ThreadLocal and initializing it with new ThreadLocal . The problem is, I really conceptually just want a persistent list that lasts the life of the thread, but I don't know if there's a way to initialize something per-thread in Java.

E.g. what I want is something like:

ThreadLocal static {
  myThreadLocalVariable.set(new ArrayList<Whatever>());
}

So that it initializes it for every thread. I know I can do this:

private static Whatever getMyVariable() {
  Whatever w = myThreadLocalVariable.get();
  if(w == null) {
    w = new ArrayList<Whatever>();
    myThreadLocalVariable.set(w);
  }
  return w; 
}

but I'd really rather not have to do a check on that every time it's used. Is there anything better I can do here?

Logos answered 12/3, 2013 at 18:41 Comment(0)
R
47

You just override the initialValue() method:

private static ThreadLocal<List<String>> myThreadLocal =
    new ThreadLocal<List<String>>() {
        @Override public List<String> initialValue() {
            return new ArrayList<String>();
        }
    };
Retake answered 12/3, 2013 at 18:43 Comment(3)
I know this Q&A is a bit old now, but as of writing, the official JDK8 docs have an example in the class description of exactly this -- using initialValue(). Ref: docs.oracle.com/javase/8/docs/api/java/lang/ThreadLocal.htmlFlorida
Also there is now a lambda-friendly static factory method that will help, e.g., ThreadLocal.withInitial(ArrayList::new)Florida
@JonSkeet I am not sure whether I should be using ThreadLocal in one of my SO question here. Wanted to see if you can help out. I haven't got any response yet and I was confuse whether using ThreadLocal is the right option.Finegrained
W
27

The accepted answer is outdated in JDK8. This is the best way since then:

private static final ThreadLocal<List<Foo>> A_LIST = 
    ThreadLocal.withInitial(ArrayList::new);
Winton answered 24/5, 2017 at 9:0 Comment(9)
Thanks. The field probably shouldn't be capitilised because it's not a constant (the list is mutable). If it were constant, there'd be no need for it to be thread local. You're also using raw types which will introduce a compiler warning. It should be ThreadLocal<List<Foo>>. :)Seeress
The field is a constant reference, doesn't matter that the referenced object is mutable.Winton
The convention is to capitalise constants, not constant references. See chapter 4 of Effective Java, which is about as authoritative as a Java reference can get: "By convention, such fields have names consisting of capital letters, with words separated by underscores. It is critical that these fields contain either primitive values or references to immutable objects. A final field containing a reference to a mutable object has all the disadvantages of a nonfinal field. While the reference cannot be modified, the referenced object can be modified—with disastrous results."Seeress
This talks about usage of constants, not the naming style. Whenever a field is static final, it is a constant. Anyway, it's just a convention. I use this one and as far as I remember, most folks do too.Winton
"Whenever a field is static final, it is a constant" hahahahaSeeress
OK, I tried to look up official definition of a constant. Oracle naming conventions don't define it. Checkstyle defines it as "static final field". Google conventions define it as "static final fields whose contents are deeply immutable". So there are contradictions. At least some people (including myself) define it as any "static final" field, so let's put aside this unrelated discussion.Winton
Constant: "a situation that does not change". Mutable: "liable to change". If you didn't want to carry on the discussion, you didn't have to reply. It's been months. However, you're wrong and I'm not letting you off the hook that easy ;)Seeress
Is public static final String TABLE_NAME = "myTable" a constant? It's a reference to a DB table, which is mutable. You can dereference it and mutate it. The distinction is set somewhat arbitrarily, that's what I say. Not that you are wrong. But many people define it as I do.Winton
Yes, of course that's a constant. It will always represent the string "myTable". The fact that it's used to access some other mutable state is irrelevant. The useful information that's conveyed by the capitalised naming convention is that the object will never change. If it will never change, I know it's inherently thread-safe and can be sure that any interaction will be free from side-effects. I can use it in a pure function. By capitalising mutable static final variables, there is no useful information conveyed about the contents at all and you're undermining the entire point of doing it.Seeress
T
20

Your solution is fine. A little simplification:

private static Whatever getMyVariable() 
{
    Whatever w = myThreadLocalVariable.get();
    if(w == null) 
        myThreadLocalVariable.set(w=new Whatever());
    return w; 
} 

In Java 8, we are able to do:

ThreadLocal<ArrayList<Whatever>> myThreadLocal = ThreadLocal.withInitial(ArrayList::new);

which uses the Supplier<T> functional interface.

Trichosis answered 12/3, 2013 at 19:0 Comment(3)
Interesting comment about Java 8's strange looking ability to initialize less verbosely.Logos
Initially it was designed as new ThreadLocal<>( lazyInitializer ). Doug Lea protested that it adds a new field to each ThreadLocal instance. The design was changed to use a factory method, which instantiates a subclass of ThreadLocal. Existing programs that use new ThreadLocal() will still get a good old ThreadLocal instance without any change.Trichosis
How to specify array list capacity or size like this ArrayList<>(size) ? in this thread local implementaiton ?Krishnakrishnah
P
0

Thread local storage is the area where each thread have it's own sperate variable so that concurrency and parallelism can be achieved.

Here is the example to initialize ThreadLocalVariable of SimpleDateFormat- object.

public class ThreadLocalTest {


private static ExecutorService threadPool = Executors.newFixedThreadPool(10);


//Thread Local SimpleDateFormat
public static ThreadLocal<SimpleDateFormat> dateFormatter = new ThreadLocal<>() {
    @Override
    protected SimpleDateFormat initialValue() {
        return new SimpleDateFormat("yyyy-MM-dd");
    }

    @Override
    public SimpleDateFormat get() {
        return super.get();
    }
};


public static void main(String[] args) throws InterruptedException {

    for (int i = 0; i < 100; i++) {

        threadPool.submit(()-> {
            String birthDate = new ThreadLocalV3().birthdate(100);
            System.out.println(birthDate);
        });

    }

    Thread.sleep(1000);
}


public String birthdate(int userId) {

    Date birthDate = new Date(); //instead of new Date()  suppose we need to fetch birthdate bu userId

    //now each thread will have it's own sdf object
    final SimpleDateFormat sdf = ThreadLocalTest.dateFormatter.get();

    return sdf.format(birthDate);
}
}

After java 8 versions, you can achieve ThreadLocal initialization using lambda expressions as below:

public static ThreadLocal<SimpleDateFormat> dateFormatter = ThreadLocal.withInitial(()-> new SimpleDateFormat("yyyy-MM-dd"));
Parliamentarian answered 13/9, 2022 at 15:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.