这个问题关注ThreadLocalRandomOpenJDK版本1.8.0的实现.
ThreadLocalRandom提供每线程随机数生成器,没有Random强加的同步开销.最明显的实现(IMO)将是这样的,它似乎保持向后兼容性而没有太多复杂性:
public class ThreadLocalRandom extends Random {
private static final ThreadLocal<ThreadLocalRandom> tl =
ThreadLocal.withInitial(ThreadLocalRandom::new);
public static ThreadLocalRandom current() {
return tl.get();
}
// Random methods moved here without synchronization
// stream methods here
}
public class Random {
private ThreadLocalRandom delegate = new ThreadLocalRandom();
// methods synchronize and delegate for backward compatibility
}
Run Code Online (Sandbox Code Playgroud)
但是,实际的实现是完全不同的,非常奇怪:
ThreadLocalRandomRandom逐字复制一些方法,稍作修改; 当然,大部分代码都可以重用.Thread 存储种子和用于初始化`ThreadLocalRandom的探测变量,违反封装;ThreadLocalRandom使用Unsafe访问的变量Thread,我想是因为这两个类在不同的包却状态变量必须是私有的Thread- Unsafe是因为封装违反唯一必要的;ThreadLocalRandom将其下nextGaussian一个存储在静态ThreadLocal而不是实例变量 …我一直在阅读ThreadLocal,试图了解它是如何工作的以及我们为什么需要它.
到目前为止,我能够学到的是以下内容:
一切似乎都很好,直到我尝试从javadoc运行示例,代码提供如下:
import java.util.concurrent.atomic.AtomicInteger;
public class UniqueThreadIdGenerator {
private static final AtomicInteger uniqueId = new AtomicInteger(0);
private static final ThreadLocal < Integer > uniqueNum =
new ThreadLocal < Integer > () {
@Override protected Integer initialValue() {
return uniqueId.getAndIncrement();
}
};
public static int getCurrentThreadId() {
return uniqueId.get();
}
} // UniqueThreadIdGenerator
Run Code Online (Sandbox Code Playgroud)
如果我正确理解了这段代码,调用getCurrentThreadId()应该返回正确的自动递增的线程号,唉它为我返回0.总是0,不考虑我已经启动了多少线程.
为了让这对我有用,我不得不改变getCurrentThreadId()来阅读
public static int getCurrentThreadId() {
return uniqueId.get();
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,我得到正确的价值观.
我的代码在下面提供,我缺少什么?(这不是javadoc实际上是错的,对吧?)
package org.vekslers;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public …Run Code Online (Sandbox Code Playgroud) 天真地,我期望ThreadLocal是某种类型的Thread的WeakHashMap.当我得知ThreadLocal的值实际上保存在Thread中的一个映射中时,我有点困惑.为什么这样做?我希望如果值保存在ThreadLocal本身中,那么与ThreadLocal相关的资源泄漏就不会存在.
澄清:我在考虑类似的事情
public class AlternativeThreadLocal<T> {
private final Map<Thread, T> values =
Collections.synchronizedMap(new WeakHashMap<Thread, T>());
public void set(T value) { values.put(Thread.currentThread(), value); }
public T get() { return values.get(Thread.currentThread());}
}
Run Code Online (Sandbox Code Playgroud)
据我所知,这可以防止一个奇怪的问题,即如果值以某种方式强烈引用ThreadLocal本身,那么ThreadLocal和它遗留的值都不会被垃圾收集,直到Thread死掉.(当ThreadLocal是值引用的类上的静态变量时,可能会出现最狡猾的形式.现在,在应用程序服务器的重新部署中存在大量资源泄漏,因为无法收集对象及其类.)
看了这个问题之后,我想我想要包装ThreadLocal来添加重置行为.
我想要一个类似于ThreadLocal的东西,我可以从任何线程调用一个方法将所有值设置回相同的值.到目前为止我有这个:
public class ThreadLocalFlag {
private ThreadLocal<Boolean> flag;
private List<Boolean> allValues = new ArrayList<Boolean>();
public ThreadLocalFlag() {
flag = new ThreadLocal<Boolean>() {
@Override protected Boolean initialValue() {
Boolean value = false;
allValues.add(value);
return value;
}
};
}
public boolean get() {
return flag.get();
}
public void set(Boolean value) {
flag.set(value);
}
public void setAll(Boolean value) {
for (Boolean tlValue : allValues) {
tlValue = value;
}
}
}
Run Code Online (Sandbox Code Playgroud)
我担心原语的自动装箱可能意味着我存储在列表中的副本将不会引用ThreadLocal引用的相同变量(如果我尝试设置它们).我还没有测试过这段代码,并且在我继续沿着这条路走下去之前,我正在寻找一些专家建议.
有人会问"你为什么这样做?".我正在一个框架中工作,其他线程回调到我的代码中,我没有对它们的引用.我想定期更新他们使用的ThreadLocal变量中的值,因此执行该更新需要使用该变量的线程进行更新.我只需要一种方法来通知所有这些线程他们的ThreadLocal变量是陈旧的.
我很高兴最近对这个三年前的问题有了新的批评,尽管我觉得它的语气比专业还要小.我提供的解决方案在此期间没有发生任何事故.然而,必然有更好的方法来实现提出这个问题的目标,我邀请评论家提供明显更好的答案.为此,我将尝试更清楚地解决我试图解决的问题.
正如我之前提到的,我正在使用一个框架,其中多个线程正在使用我的代码,在我的控制之外.该框架是QuickFIX/J,我正在实现Application接口.该接口定义了用于处理FIX消息的钩子,在我的使用中,框架被配置为多线程,因此可以同时处理与应用程序的每个FIX连接.
但是,QuickFIX/J框架仅为所有线程使用该接口的单个实例实例.我无法控制线程如何启动,并且每个都在为不同的连接提供不同的配置细节和其他状态.很自然地让一些经常访问但很少更新的状态存在于ThreadLocal框架启动线程后加载其初始值的各种s中. …