如何在没有实际执行的情况下测试ThreadLocal是否已初始化?

我想测试ThreadLocal是否已经初始化而没有实际初始化它。 当然,代码需要是线程安全的。 理想情况下我想要这样的东西:

 class TestableThreadLocal extends ThreadLocal { public boolean isInitialized() { ... } } 

但是我该如何实现这种方法呢?

编辑:动机:我已经将一个ThreadLocal子类ThreadLocal覆盖initialValue() 。 但是,我并不总是需要初始化,特别是因为它可能会导致多类加载器环境中的内存泄漏。 一个简单的测试可以帮助我编写代码以避免意外初始化。

我倾向于使用简单的规则来避免类加载器泄漏,不可否认它们很麻烦,但有一些包装并没有那么糟糕。 是的,泄漏是邪恶的。

  • 永远不要覆盖initialValue – 你只是在寻找麻烦,只是忘了它存在。
  • 除非你做threadLocal.set(xxx); try{...process...}finally{threadLocal.set(null);}否则不要在ThreadLocal中存储非系统/非bootstap类threadLocal.set(xxx); try{...process...}finally{threadLocal.set(null);} threadLocal.set(xxx); try{...process...}finally{threadLocal.set(null);}
  • 如果仍然覆盖initialValue,请使用threadLocal.remove()而不是threadLocal.set(null)
  • 或者使用WeakReferene (即ThreadLocal > )作为保留硬引用的池中的值。 它可能看起来很直观但是一旦清除了池,值就会消失,并且类可以自由地进行GC

我意识到post不是对你的问题的直接回复,但是没有简单的方法来实现你想要的东西并防止漏洞。

思考:

  • 如果调用ThreadLocal.get ,则初始化该值。
  • 如果你不想初始化它,你不需要存储的值(根本不存在)
  • 如果你想避免初始化,似乎开始的地方是重写get ,而不是initialValue
  • 采取不同的轨道 – 你真的需要inheritanceThreadLocal而不是收容吗?

执行此操作的几种可能方法之一将涉及reflection,因为ThreadLocal没有可让您知道值是否已初始化的API。

当然可以使用reflection“编码”它。 但是,所有常见的警告都适用。 您的reflection代码将高度依赖于java.lang.ThreadLocal及其非公共成员的实现细节。 JDK供应商改变实施的那一刻,你的代码就会崩溃。

你对最初的问题感到困惑。 ThreadLocal是您正在创建的还是已由其他人提供的API?

如果您正在创建ThreadLocal,那么我假设您已经知道必须覆盖initialValue()以便您可以提供初始值。

在这种情况下,您始终可以跟踪是否已调用该值并已创建该值。

我认为下面这段代码会做你想要的:

 MyThreadLocal extends ThreadLocal{ Map myThreadMap = new HashMap(); protected T initialValue(){ synchronized(myThreadMap){ T value = ... your code to create initial value myThreadMap.put(Thread.currentThread(),true); return T; } } public boolean containsThreadValues(){ synchronized(myThreadMap){ return myThreadMap.isEmpty(); } } public boolean isInitializedForThisThread(){ synchronized(myThreadMap){ return myThreadMap.get(Thread.currentThread())==true; } } } 

myThreadMap将始终包含旧密钥,即使对于死线程也是如此。 如果要清除旧密钥,则将其替换为WeakHashMap。 然后,当使用ThreadLocal的线程消失时,键将被删除。