package com.alibaba.ttl;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.Callable;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* {@link TransmittableThreadLocal} can transmit value from the thread of submitting task to the thread of executing task.
*
* Note:
* {@link TransmittableThreadLocal} extends {@link InheritableThreadLocal},
* so {@link TransmittableThreadLocal} first is a {@link InheritableThreadLocal}.
* If the inheritable ability from {@link InheritableThreadLocal} has potential leaking problem,
* you can disable the inheritable ability:
*
* ❶ For thread pooling components({@link java.util.concurrent.ThreadPoolExecutor},
* {@link java.util.concurrent.ForkJoinPool}), Inheritable feature should never happen,
* since threads in thread pooling components is pre-created and pooled, these threads is neutral to biz logic/data.
*
* Disable inheritable for thread pooling components by wrapping thread factories using methods
* {@link com.alibaba.ttl.threadpool.TtlExecutors#getDisableInheritableThreadFactory(java.util.concurrent.ThreadFactory) getDisableInheritableThreadFactory} /
* {@link com.alibaba.ttl.threadpool.TtlForkJoinPoolHelper#getDefaultDisableInheritableForkJoinWorkerThreadFactory() getDefaultDisableInheritableForkJoinWorkerThreadFactory}.
*
* Or you can turn on "disable inheritable for thread pool" by {@link com.alibaba.ttl.threadpool.agent.TtlAgent}
* so as to wrap thread factories for thread pooling components automatically and transparently.
*
* ❷ In other cases, disable inheritable by overriding method {@link #childValue(Object)}.
*
* Whether the value should be inheritable or not can be controlled by the data owner,
* disable it carefully when data owner have a clear idea.
*
* More discussion about "disable the inheritable ability"
* see
* issue #100: disable Inheritable when it's not necessary and buggy.
*
* @author Jerry Lee (oldratlee at gmail dot com)
* @author Yang Fang (snoop dot fy at gmail dot com)
* @see TtlRunnable
* @see TtlCallable
* @see com.alibaba.ttl.threadpool.TtlExecutors
* @see com.alibaba.ttl.threadpool.TtlExecutors#getTtlExecutor(java.util.concurrent.Executor)
* @see com.alibaba.ttl.threadpool.TtlExecutors#getTtlExecutorService(java.util.concurrent.ExecutorService)
* @see com.alibaba.ttl.threadpool.TtlExecutors#getTtlScheduledExecutorService(java.util.concurrent.ScheduledExecutorService)
* @see com.alibaba.ttl.threadpool.TtlExecutors#getDefaultDisableInheritableThreadFactory()
* @see com.alibaba.ttl.threadpool.TtlExecutors#getDisableInheritableThreadFactory(java.util.concurrent.ThreadFactory)
* @see com.alibaba.ttl.threadpool.TtlForkJoinPoolHelper
* @see com.alibaba.ttl.threadpool.TtlForkJoinPoolHelper#getDefaultDisableInheritableForkJoinWorkerThreadFactory()
* @see com.alibaba.ttl.threadpool.TtlForkJoinPoolHelper#getDisableInheritableForkJoinWorkerThreadFactory(java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory)
* @see com.alibaba.ttl.threadpool.agent.TtlAgent
* @since 0.10.0
*/
public class TransmittableThreadLocal extends InheritableThreadLocal implements TtlCopier {
private static final Logger logger = Logger.getLogger(TransmittableThreadLocal.class.getName());
private final boolean disableIgnoreNullValueSemantics;
/**
* Default constructor. Create a {@link TransmittableThreadLocal} instance with "Ignore-Null-Value Semantics".
*
* About "Ignore-Null-Value Semantics":
*
*
*
If value is {@code null}(check by {@link #get()} method), do NOT transmit this {@code ThreadLocal}.
*
If set {@code null} value, also remove value(invoke {@link #remove()} method).
*
*
* This is a pragmatic design decision:
*
*
use explicit value type rather than {@code null} value to express biz intent.
*
safer and more robust code(avoid {@code NPE} risk).
*
*
* So it's strongly not recommended to use {@code null} value.
*
* But the behavior of "Ignore-Null-Value Semantics" is NOT compatible with
* {@link ThreadLocal} and {@link InheritableThreadLocal},
* you can disable this behavior/semantics via using constructor {@link #TransmittableThreadLocal(boolean)}
* and setting parameter {@code disableIgnoreNullValueSemantics} to {@code true}.
*
* More discussion about "Ignore-Null-Value Semantics" see
* Issue #157.
*
* @see #TransmittableThreadLocal(boolean)
*/
public TransmittableThreadLocal() {
this(false);
}
/**
* Constructor, create a {@link TransmittableThreadLocal} instance
* with parameter {@code disableIgnoreNullValueSemantics} to control "Ignore-Null-Value Semantics".
*
* @param disableIgnoreNullValueSemantics disable "Ignore-Null-Value Semantics"
* @see #TransmittableThreadLocal()
* @since 2.11.3
*/
public TransmittableThreadLocal(boolean disableIgnoreNullValueSemantics) {
this.disableIgnoreNullValueSemantics = disableIgnoreNullValueSemantics;
}
/**
* Computes the value for this transmittable thread-local variable
* as a function of the source thread's value at the time the task
* Object is created.
*
* This method is called from {@link TtlRunnable} or
* {@link TtlCallable} when it create, before the task is started.
*
* This method merely returns reference of its source thread value(the shadow copy),
* and should be overridden if a different behavior is desired.
*
* @since 1.0.0
*/
public T copy(T parentValue) {
return parentValue;
}
/**
* Callback method before task object({@link TtlRunnable}/{@link TtlCallable}) execute.
*
* Default behavior is to do nothing, and should be overridden
* if a different behavior is desired.
*
* Do not throw any exception, just ignored.
*
* @since 1.2.0
*/
protected void beforeExecute() {
}
/**
* Callback method after task object({@link TtlRunnable}/{@link TtlCallable}) execute.
*
* Default behavior is to do nothing, and should be overridden
* if a different behavior is desired.
*
* Do not throw any exception, just ignored.
*
* @since 1.2.0
*/
protected void afterExecute() {
}
/**
* see {@link InheritableThreadLocal#get()}
*/
@Override
public final T get() {
T value = super.get();
if (disableIgnoreNullValueSemantics || null != value) addThisToHolder();
return value;
}
/**
* see {@link InheritableThreadLocal#set}
*/
@Override
public final void set(T value) {
if (!disableIgnoreNullValueSemantics && null == value) {
// may set null to remove value
remove();
} else {
super.set(value);
addThisToHolder();
}
}
/**
* see {@link InheritableThreadLocal#remove()}
*/
@Override
public final void remove() {
removeThisFromHolder();
super.remove();
}
private void superRemove() {
super.remove();
}
private T copyValue() {
return copy(get());
}
// Note about the holder:
// 1. holder self is a InheritableThreadLocal(a *ThreadLocal*).
// 2. The type of value in the holder is WeakHashMap, ?>.
// 2.1 but the WeakHashMap is used as a *Set*:
// the value of WeakHashMap is *always* null, and never used.
// 2.2 WeakHashMap support *null* value.
private static final InheritableThreadLocal, ?>> holder =
new InheritableThreadLocal, ?>>() {
@Override
protected WeakHashMap, ?> initialValue() {
return new WeakHashMap, Object>();
}
@Override
protected WeakHashMap, ?> childValue(WeakHashMap, ?> parentValue) {
return new WeakHashMap, Object>(parentValue);
}
};
@SuppressWarnings("unchecked")
private void addThisToHolder() {
if (!holder.get().containsKey(this)) {
holder.get().put((TransmittableThreadLocal