【发布时间】:2021-07-27 17:13:44
【问题描述】:
用例
我有一个基于 gRPC+Guice 的服务应用程序,其中对于特定调用,代码流如下所示:A -> B -> C 和 A -> X -> Y 对于特定服务请求。
其中,A = 顶级服务操作/Activity 类; B = 以 C 类作为任务创建 ExecutorService 线程池的类; X 和 Y 是普通类。
我想要一个跨类 B、C 和 Y 这些类的共享对象 ContainerDoc,但不想传递给方法参数。所以,我决定使用 InheritableThreadLocal。
但我想了解如何强制将父 ThreadLocal ContainerDoc 共享给子线程,以便子线程在 ContainerDoc 中完成的任何 更新 对父线程也可见?
- 重写 childValue 方法 是否返回与父对象相同的包含对象以使其工作? (见下文实施)。
- 如何确保线程安全?
示例实施
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();
}
}
客户端代码(来自子线程类或常规类
MyThreadLocalInfo.THREADLOCAL_ABC.setDoc(new ContainerDoc());
MyThreadLocalInfo.THREADLOCAL_ABC.getDoc().put(Key_A, new Object());
MyThreadLocalInfo.THREADLOCAL_ABC.clearDoc();
【问题讨论】:
标签: java multithreading concurrency thread-local inheritable-thread-local