Use case
I have a gRPC+Guice based service application where for a particular call the code flow looks like: A -> B -> C
and A -> X -> Y
for a particular service request.
where, A = Top level Service operation/Activity class; B = Class which creates ExecutorService Threadpool with class C as task; X and Y are normal classes.
I want a shared object ContainerDoc
across class B, C and Y these classes but do not want to pass on to method parameters. So, I have decided to use InheritableThreadLocal.
But I want to understand how to enforce sharing the parent ThreadLocal ContainerDoc
to the Child Threads, so that any updates done in the ContainerDoc
by child Thread is also visible to parent thread?
- Does overriding childValue method to return same contained object as of parent to make it work? (See below implementation).
- How to ensure Thread-Safety?
Sample Implementation
class ContainerDoc implements ServiceDoc {
private final Map < KeyEnum, Object > containerMap;
public ContainerDoc() {
this.containerMap = new HashMap < KeyEnum, Object > ();
// Should it be ConcurrentHashmap to account for concurrent updates?
}
public < T > T getEntity(final KeyEnum keyEnum) {
return (T) containerMap.get(keyEnum);
}
public void putEntity(final KeyEnum keyEnum, final Object value) {
entities.put(keyEnum, value);
}
enum KeyEnum {
Key_A,
Key_B;
}
}
public enum MyThreadLocalInfo {
THREADLOCAL_ABC(ContainerDoc.class, new InheritableThreadLocal < ServiceDoc > () {
// Sets the initial value to an empty class instance.
@Override
protected ServiceContext initialValue() {
return new ContainerDoc();
}
// Just for reference. The below impl shows default
// behavior. This method is invoked when any new
// thread is created from a parent thread.
// This ensures every child thread will have same
// reference as parent.
@Override
protected ServiceContext childValue(final ServiceDoc parentValue) {
return parentValue;
// Returning same reference but I think this
// value gets copied over to each Child thread as
// separate object instead of reusing the same
// copy for thread-safety. So, how to ensure
// using the same reference as of parent thread?
}
}),
THREADLOCAL_XYZ(ABC.class, new InheritableThreadLocal < ServiceDoc > () {
....
....
});
private final Class << ? extends ServiceDoc > contextClazz;
private final InheritableThreadLocal < ServiceDoc > threadLocal;
MyThreadLocalInfo(final Class << ? extends ServiceDoc > contextClazz,
final InheritableThreadLocal < ServiceDoc > threadLocal) {
this.contextClazz = contextClazz;
this.threadLocal = threadLocal;
}
public ServiceDoc getDoc() {
return threadLocal.get();
}
public void setDoc(final ServiceDoc serviceDoc) {
Validate.isTrue(contextClazz.isAssignableFrom(serviceDoc.getClass()));
threadLocal.set(serviceDoc);
}
public void clearDoc() {
threadLocal.remove();
}
}
Client code (from Child Thread class or regular class
MyThreadLocalInfo.THREADLOCAL_ABC.setDoc(new ContainerDoc());
MyThreadLocalInfo.THREADLOCAL_ABC.getDoc().put(Key_A, new Object());
MyThreadLocalInfo.THREADLOCAL_ABC.clearDoc();