com.github.phantomthief.scope.Scope Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of scope Show documentation
Show all versions of scope Show documentation
A thread local based scope api preventing thread local leaking.
package com.github.phantomthief.scope;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.github.phantomthief.util.ThrowableRunnable;
import com.github.phantomthief.util.ThrowableSupplier;
/**
* 自定义Scope,支持如下功能:
*
*
* - 开启一个自定义的Scope,在Scope范围内,可以通过 {@link Scope} 各个方法读写数据
* - 可以通过 {@link #supplyWithExistScope} 或者 {@link #runWithExistScope} 绑定已经存在的scope
*
*
* 举个栗子:
* {@code
* ScopeKey<String> TEST_KEY = allocate();
*
* runWithNewScope(() -> {
* TEST_KEY.set("abc");
* String result = TEST_KEY.get(); // get "abc"
*
* Scope scope = getCurrentScope();
* executor.execute(wrapRunnableExistScope(scope, () -> {
* String resultInScope = TEST_KEY.get(); // get "abc"
* });
* });
* }
*
* TODO: 当前实现是一种比较简易的方式,直接把所有Scope放到一个ThreadLocal里。
* 实际上这样在使用过程中会有二次hash查询的问题,对性能会有些许的影响,更好的做法是:
* 直接使用ThreadLocal(也就是使用内部的ThreadLocalMap),同时在Scope拷贝和清理时,维护一个额外的Set,进行ThreadLocal拷贝。
*
* 这样的优化考虑是:Scope正常访问的频率很高,而线程切换拷贝的概率比较低。
* 目前这个实现参考了 GRPC 的 Context API 以及 Spring 的 RequestContext,
* 相对比较简单,目前效率也可以接受。等到需要榨取性能时再对这个实现动手吧。
*
* @author w.vela
*/
public final class Scope {
private static final ThreadLocal SCOPE_THREAD_LOCAL = new ThreadLocal<>();
private final ConcurrentMap, Object> values = new ConcurrentHashMap<>();
public static void runWithExistScope(@Nullable Scope scope,
ThrowableRunnable runnable) throws X {
supplyWithExistScope(scope, () -> {
runnable.run();
return null;
});
}
public static T supplyWithExistScope(@Nullable Scope scope,
ThrowableSupplier supplier) throws X {
Scope oldScope = SCOPE_THREAD_LOCAL.get();
SCOPE_THREAD_LOCAL.set(scope);
try {
return supplier.get();
} finally {
if (oldScope != null) {
SCOPE_THREAD_LOCAL.set(oldScope);
} else {
SCOPE_THREAD_LOCAL.remove();
}
}
}
public static void runWithNewScope(@Nonnull ThrowableRunnable runnable)
throws X {
supplyWithNewScope(() -> {
runnable.run();
return null;
});
}
public static T
supplyWithNewScope(@Nonnull ThrowableSupplier supplier) throws X {
beginScope();
try {
return supplier.get();
} finally {
endScope();
}
}
/**
* 正常应该优先使用 {@link #supplyWithNewScope} 或者 {@link #runWithNewScope}
*
* 手工使用beginScope和endScope的场景只有在:
*
* - 上面两个方法当需要抛出多个正交异常时会造成不必要的try/catch代码
* - 开始scope和结束scope不在一个代码块中
*
*
* @throws IllegalStateException if try to start a new scope in an exist scope.
*/
@Nonnull
public static Scope beginScope() {
Scope scope = SCOPE_THREAD_LOCAL.get();
if (scope != null) {
throw new IllegalStateException("start a scope in an exist scope.");
}
scope = new Scope();
SCOPE_THREAD_LOCAL.set(scope);
return scope;
}
/**
* @see #beginScope
*/
public static void endScope() {
SCOPE_THREAD_LOCAL.remove();
}
@Nullable
public static Scope getCurrentScope() {
return SCOPE_THREAD_LOCAL.get();
}
public void set(@Nonnull ScopeKey key, T value) {
if (value != null) {
values.put(key, value);
} else {
values.remove(key);
}
}
@SuppressWarnings("unchecked")
public T get(@Nonnull ScopeKey key) {
T value = (T) values.get(key);
if (value == null && key.initializer() != null) {
// 这里不使用computeIfAbsent保证原子性,是因为computeIfAbsent会有几率造成同桶冲撞
// 而实际上,这里的原子性意义不大,就不浪费时间和精力了
value = key.initializer().get();
if (value != null) {
values.put(key, value);
}
}
return value == null ? key.defaultValue() : value;
}
}