I'm running a web service which is receiving 200 RPS at least. Based on the action, we provide root access for few operations and using the following code.
private static final ThreadLocal<String> rootContext = new ThreadLocal<String>();
public Optional<String> getRunner() {
if (rootContext.get() != null) {
return rootContext.get();
} else {
return getCurrentRunner();
}
}
public void rootAccess(Runnable runnable) {
rootContext.set("root");
runnable.run();
rootContext.set(null);
}
getCurrentRunner()
method will return the actual caller based on the request. The problem is 1 request out of 200 requests returns root
instead of the actual caller.
One thing I noticed is instead of using threadlocal.remove(), I'm setting that value as null. Expecting that, getRunner() rootContext.get() != null
condition will fail and return the actual caller.
How to solve this ? Will setting rootContext.remove()
solve this ? If yes, how ?
Thanks for the help
There are two problems with your rootAccess
method:
rootContext.set(null);
still keeps the ThreadLocal
instance associated with the running thread, it is better to do rootContext.remove();
Correcting this two points means to change rootAccess()
to
public void rootAccess(Runnable runnable) {
rootContext.set("root");
try {
runnable.run();
} finally {
rootContext.remove();
}
}
Why is rootContext.set(null);
generally a problem?
Each thread basically keeps a data structure similar to a Map<ThreadLocal, ?>
where the key is your ThreadLocal
instance (rootContext
) and the value is the value that you associate with it through rootContext.set(xx);
If you call rootContext.set(null);
then rootContext
is still in that map and therefore each thread (from a thread pool, meaning the thread is long running) that executed this line keeps a reference to rootContext
which in turn might prevent class unloading.
If you call rootContext.remove();
the rootContext
is removed from that map.