我正在创建一个具有以下特征的memoization缓存:
什么会有一个优越的性能,或在什么条件下一个解决方案优于另一个解决方案?
ThreadLocal HashMap:
class MyCache {
private static class LocalMyCache {
final Map<K,V> map = new HashMap<K,V>();
V get(K key) {
V val = map.get(key);
if (val == null) {
val = computeVal(key);
map.put(key, val);
}
return val;
}
}
private final ThreadLocal<LocalMyCache> localCaches = new ThreadLocal<LocalMyCache>() {
protected LocalMyCache initialValue() {
return new LocalMyCache();
}
};
public V get(K key) {
return localCaches.get().get(key);
}
}
Run Code Online (Sandbox Code Playgroud)
ConcurrentHashMap的: …
标题可能有点强,但让我解释一下我是如何理解会发生什么的.我猜这发生在Tomcat上(引用的消息来自Tomcat),但我不确定了.
TL; DR在底部有一个总结,为什么我声称这是Web服务器的错.
我可能错了(但没有错误的可能性,没有理由问):
ThreadLocalThreadLocal指物体从库ClassLoader网络服务器
如果我理解正确,重新部署后旧的"脏"线程继续被重用.它们ThreadLocal引用了引用它们的旧类,它们ClassLoader指的是整个旧的类层次结构.因此PermGen,随着时间的推移,很多东西都停留在空间中OutOfMemoryError.到目前为止这是对的吗?
我假设有两件事:
因此,在每次重新部署一个完整的线程池更新费一毫秒的一小部分几次每小时,也就是说,还有的时间开销0.0001 * 12/3600 * 100%,即0.000033%.
但是,不是接受这个微小的开销,而是有无数的问题.我的计算错了还是我忽略了什么?
作为警告,我们得到了消息
Web应用程序...使用类型为...的键创建了一个ThreadLocal,并且值为...但在Web应用程序停止时无法将其删除.
应该更好地说明
Web服务器...使用线程池但在停止(或重新部署)应用程序后无法续订.
还是我错了?即使所有线程不时重新创建,时间开销也可以忽略不计.但是ThreadLocal在将它们提供给应用程序之前清除它们就足够了,甚至更快.
有一些真正的问题(最近这个问题),用户无能为力.图书馆作家有时可以而且有时不能.恕我直言,网络服务器可以很容易地解决它.事情发生了并且有原因.所以我责怪唯一一个可以对此采取任何行动的政党.
这个问题的标题比正确的更具挑衅性,但它有其重点.raphw的答案也是如此.这个相关问题有另一个开放的赏金.
我认为Web服务器可以解决它如下:
LastCleanupTimestamp在a中存储a ThreadLocal(对于新线程,它是创建时间)delta,例如1小时)ThreadLocals并设置一个新的LastCleanupTimestamp这将确保没有这样的泄漏存在的时间长于delta加上最长请求的持续时间加上线程周转时间.费用如下:
ThreadLocal每个请求检查一个(即几纳秒) …在Java 8中,java.lang.Thread类有3个新字段:
/** The current seed for a ThreadLocalRandom */
@sun.misc.Contended("tlr")
long threadLocalRandomSeed;
/** Probe hash value; nonzero if threadLocalRandomSeed initialized */
@sun.misc.Contended("tlr")
int threadLocalRandomProbe;
/** Secondary seed isolated from public ThreadLocalRandom sequence */
@sun.misc.Contended("tlr")
int threadLocalRandomSecondarySeed;
Run Code Online (Sandbox Code Playgroud)
正如它在Javadoc中所说的那样是由专门管理的java.util.concurrent.ThreadLocalRandom.
此外,ThreadLocalRandom它们以非常奇特的方式使用:
SEED = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomSeed"));
PROBE = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomProbe"));
SECONDARY = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomSecondarySeed"));
Run Code Online (Sandbox Code Playgroud)
(同样的代码片也可以在LockSupport课堂上得到满足).
然后这个偏移在几个java.concurrent地方内部使用.
这个想法是什么?为什么这些字段在里面java.lang.Thread?为什么不在里面ThreadLocalRandom?
我在下面的博客上看到了ThreadLocals的概念:
https://www.baeldung.com/java-threadlocal
它说"不要将ThreadLocal与ExecutorService一起使用"
它说明了下面使用ThreadLocals的示例.
public class ThreadLocalWithUserContext implements Runnable {
private static ThreadLocal<Context> userContext
= new ThreadLocal<>();
private Integer userId;
private UserRepository userRepository = new UserRepository();
@Override
public void run() {
String userName = userRepository.getUserNameForUserId(userId);
userContext.set(new Context(userName));
System.out.println("thread context for given userId: "
+ userId + " is: " + userContext.get());
}
// standard constructor
}
Run Code Online (Sandbox Code Playgroud)
在帖子的最后,它提到:
"如果我们想使用ExecutorService并向其提交Runnable,那么使用ThreadLocal将产生非确定性结果 - 因为我们无法保证每次给定userId的每个Runnable操作都将由同一个线程处理执行.
因此,我们的ThreadLocal将在不同的userId之间共享.这就是为什么我们不应该将TheadLocal与ExecutorService一起使用.只有当我们完全控制哪个线程将选择要执行哪个可运行动作时,才应该使用它."
这个解释对我来说是一个保镖.我特意在网上做了一些研究,但是我得不到多少帮助,有些专家请详细说明上述解释吗?它是作者观点还是真正的威胁?
我注意到这个ThreadLocal<T>实现IDisposable,暗示我在使用它时应该处理一个线程局部变量.我只是好奇具体的问题是什么,我应该小心做什么和/或避免做什么.
当线程退出时,线程的本地存储是否会被丢弃?如果我不处理我的ThreadLocal成员,最糟糕的情况是什么?
如果我有一个全局线程局部(oxymoron?hehe)变量(或者一个ThreadStatic变量)并且我在ThreadPool中的线程上赋值这个怎么办?我是否必须小心取消分配线程本地值,或者这不是一个问题?
在负载测试期间测试Tomcat NIO连接器时,我们想到了这一点.我使用ThreadLocal另外我使用Spring,我知道在几个地方它也使用它.
由于NIO连接器每个连接没有一个线程,我担心如果ThreadLocal对象在清理之前与另一个线程共享,则可能导致很难找到错误.但是,我认为这不是一个问题,因为它不是我能找到的文件警告,也没有发现任何其他帖子警告这一点.我假设NIO连接器对服务于实际请求的线程没有影响.
在我采用这个假设之前,我希望找到一些具体的证据.
备受好评的书籍JCIP对ThreadLocal的使用说明了这一点:
通过将其线程限制属性视为使用全局变量的许可或作为创建"隐藏"方法参数的方法,很容易滥用ThreadLocal.线程局部变量可能会降低可重用性并在类之间引入隐藏的耦合,因此应谨慎使用.
说Thread-local变量可以减少可重用性并在类之间引入隐藏的耦合是什么意思?
java concurrency multithreading synchronization thread-local
来自Java Concurrency in practice Chapter 3.3.3.ThreadLocal的
线程局部变量通常用于防止基于可变单元或全局变量的设计中的共享.
如果我们在ThreadLocal中包装可变的Singleton人,每个线程都有自己的Singleton副本?那么它将如何保持单身?这是作者的意思还是我错过了一些非常明显的东西?
我在Java中探索Thread本地.我无法理解为什么我们需要这门课.如果我只是简单地将一个新对象传递给每个线程来执行,我可以实现相同的动机,如果我使用initialValue()就会发生同样的事情.我只是在initialvalue()中为每个线程返回一个新对象.
但是说我有两个线程,ThreadOne:A和ThreadTwo B.现在我希望他们拥有自己的SimpleDateFormat类的副本.我可以通过在ThreadLocal类中扭曲SimpleDateFormat的对象然后使用initialValue()来执行此操作,我可以返回新的SimpleDateFormat("yyyyMMdd HHmm");. 同样的动机我可以通过创建两个新的SimpleDateFormat对象来实现,并且[每个都向ThreadOne提供一个:A.和ThreadTwo:B.ThreadLocal如何帮助我额外的
问候,
这是我偶然发现的代码:
class TransactionContextHolder {
private static final ThreadLocal<TransactionContext> currentTransactionContext = new NamedInheritableThreadLocal<TransactionContext>(
"Test Transaction Context");
static TransactionContext getCurrentTransactionContext() {
return currentTransactionContext.get();
}
static void setCurrentTransactionContext(TransactionContext transactionContext) {
currentTransactionContext.set(transactionContext);
}
static TransactionContext removeCurrentTransactionContext() {
synchronized (currentTransactionContext) {
TransactionContext transactionContext = currentTransactionContext.get();
currentTransactionContext.remove();
return transactionContext;
}
}
Run Code Online (Sandbox Code Playgroud)
}
所述currentTransactionContext字段是类型的ThreadLocal的,它是在该类中唯一的字段.
在我看来,这里不需要同步,因为存储在ThreadLocal中的值与特定线程相关联,因此它不是共享状态.另外我认为它会影响性能,因为currentTransactionContext本身是共享的,只允许一个线程进入块,而许多线程可以并行执行而不影响正确性.
这里需要同步吗?
thread-local ×10
java ×9
performance ×2
tomcat ×2
.net ×1
c# ×1
caching ×1
concurrency ×1
java-8 ×1
memory-leaks ×1
nio ×1
synchronized ×1
webserver ×1