All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.phantomthief.scope.Scope Maven / Gradle / Ivy

There is a newer version: 1.0.23
Show newest version
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; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy