我对ThreadLocal的有限理解是它存在资源泄漏问题.我收集这个问题可以通过在ThreadLocal中正确使用WeakReferences来解决(尽管我可能误解了这一点.)我只是想要一个模式或示例来正确使用带有WeakReference的ThreadLocal(如果存在).例如,在此代码片段中,将引入WeakReference?
static class DateTimeFormatter {
private static final ThreadLocal<SimpleDateFormat> DATE_PARSER_THREAD_LOCAL = new ThreadLocal<SimpleDateFormat>() {
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy/MM/dd HH:mmz");
}
};
public String format(final Date date) {
return DATE_PARSER_THREAD_LOCAL.get().format(date);
}
public Date parse(final String date) throws ParseException
{
return DATE_PARSER_THREAD_LOCAL.get().parse(date);
}
}
Run Code Online (Sandbox Code Playgroud) 我最近意识到线程本地存储在某些平台上是有限的.例如,C++库boost :: thread read的文档:
"注意:可以创建的特定于线程的存储对象的数量存在特定于实现的限制,并且此限制可能很小."
我一直在寻找尝试找出不同平台的限制,但我找不到权威表.如果您正在编写使用TLS的跨平台应用程序,这是一个重要问题.Linux是我找到信息的唯一平台,以Ingo Monar补丁的形式在2002年发送到内核列表添加TLS支持,他提到"TLS区域的数量是无限的,并且没有相关的额外分配开销支持TLS." 如果在2009年仍然如此(是吗?)非常漂亮.
但是今天Linux怎么样?OS X?视窗?的Solaris?嵌入式操作系统?对于在多种体系结构上运行的操作系统,它是否因架构而异?
编辑:如果您对可能存在限制的原因感到好奇,请考虑预先分配线程本地存储的空间,因此您将在每个线程上支付费用.面对很多线程,即使是少量也是一个问题.
规范是否保证顺序 Java Streams 上的所有操作都在当前线程中执行?("forEach"和"forEachOrdered"除外)
我明确要求规范,而不是当前的实现.我可以自己查看当前的实现,不需要打扰你.但实现可能会改变,还有其他实现.
我问因为ThreadLocals:我使用的是一个内部使用ThreadLocals的Framework.即使像company.getName()这样的简单调用最终也会使用ThreadLocal.我无法改变该框架的设计方式.至少不在理智的时间内.
这里的规范似乎令人困惑.包"java.util.stream"的文档说明:
如果行为参数确实有副作用,除非明确说明,否则不能保证这些副作用对其他线程的可见性,也不保证同一流管道中"相同"元素的不同操作在同一个线程中执行.
...
即使当管道被约束以产生结果,其与流源的遭遇顺序一致(例如,IntStream.range(0,5).parallel()地图(X - > X*2).toArray( )必须产生[0,2,4,6,8]),不能保证制成,以作为对于所述映射器函数被应用到各个元件,或为了在什么螺纹任何行为参数被执行对于给定的元件.
我将其解释为:流上的每个操作都可以在不同的线程中发生.但是"forEach"和"forEachOrdered"的文档明确指出:
对于任何给定元素,可以在任何时间以及库选择的任何线程中执行该动作.
如果每个流操作都可能发生在未指定的线程中,那么该语句将是多余的.因此恰恰相反:串行流上的所有操作都保证在当前线程中执行,除了"forEach"和"forEachOrdered"?
我搜索了关于"Java","Stream"和"ThreadLocal"组合的权威答案,但没有发现任何内容.该关闭的事情是由一个答案作者Brian Goetz在这里对堆栈溢出一个相关的问题,但它是有关订单,而不是线程,它只是对"的forEach",而不是其他流的方法:是否Stream.forEach尊重遇到顺序流的顺序?
JEP-425:虚拟线程指出“应该为每个应用程序任务创建一个新的虚拟线程”,并两次提到在 JVM 中运行“数百万”虚拟线程的可能性。
相同的 JEP 意味着每个虚拟线程都可以访问其自己的线程本地值:
虚拟线程就像平台线程一样支持线程局部变量,因此它们可以运行使用线程局部变量的现有代码。
线程局部变量多次用于缓存非线程安全且创建成本昂贵的对象。JEP 警告:
但是,由于虚拟线程可能非常多,因此请在仔细考虑后使用线程局部变量。
确实很多!特别是考虑到虚拟线程不被池化(或者至少不应该被池化)。作为短期任务的代表,在虚拟线程中使用线程局部变量来缓存昂贵的对象似乎毫无意义。除非!我们可以从虚拟线程创建并访问绑定到其载体线程的线程局部变量
为了澄清起见,我想从这样的事情开始(当仅使用上限为池大小的本机线程时,这是完全可以接受的,但是当连续运行数百万个虚拟线程时,这显然不再是一个非常有效的缓存机制重新创建:
static final ThreadLocal<DateFormat> CACHED = ThreadLocal.withInitial(DateFormat::getInstance);
Run Code Online (Sandbox Code Playgroud)
为此(可惜这个类不是公共 API 的一部分):
static final ThreadLocal<DateFormat> CACHED = new jdk.internal.misc.CarrierThreadLocal();
// CACHED.set(...)
Run Code Online (Sandbox Code Playgroud)
在我们到达那里之前。人们必须问,这是一种安全的做法吗?
好吧,据我正确理解虚拟线程,它们只是在平台线程(又名“载体线程”)上执行的逻辑阶段,能够卸载而不是被阻塞等待。所以我假设 - 如果我错了,请纠正我 - 1)虚拟线程永远不会被同一载体线程上的另一个虚拟线程交错或重新安排在另一个载体线程上,除非代码否则会阻塞,因此,如果 2 )我们对缓存对象调用的操作永远不会阻塞,那么任务/虚拟线程将简单地在同一载体上从头到尾运行,所以是的,将对象缓存在平台线程本地上是安全的。
冒着回答我自己的问题的风险,JEP-425 表明这是不可能的:
载体的线程局部变量对于虚拟线程不可用,反之亦然。
我找不到公共 API 来获取载体线程或在平台线程上显式分配线程局部变量(从虚拟线程),但这并不是说我的研究是确定的。也许有办法吗?
然后我读了JEP-429: Scoped Values,乍一看似乎是 Java 之神试图完全摆脱它ThreadLocal,或者至少为虚拟线程提供替代方案。事实上,JEP 使用了诸如“迁移到作用域值”之类的措辞,并表示它们“优于线程局部变量,尤其是在使用大量虚拟线程时”。
对于 JEP 中讨论的所有用例,我只能同意。但在本文档的底部,我们还发现了这一点:
有一些场景有利于线程局部变量。一个示例是缓存创建和使用成本高昂的对象,例如 java.text.DateFormat 的实例。众所周知,DateFormat 对象是可变的,因此如果没有同步,就无法在线程之间共享它。通过在线程生命周期内持续存在的线程局部变量为每个线程提供自己的 DateFormat 对象通常是一种实用的方法。
根据前面讨论的内容,使用线程本地可能是“实用的”,但不是很理想。事实上,JEP-429 本身实际上是从一个非常有说服力的评论开始的:“如果一百万个虚拟线程中的每一个都有可变的线程局部变量,那么内存占用可能会很大”。
总结一下:
您是否找到了从虚拟线程在载体线程上分配线程局部变量的方法?
如果不是,那么可以肯定地说,对于使用虚拟线程的应用程序,在线程本地缓存对象的做法已经死亡,并且必须实现/使用不同的方法,例如并发缓存/映射/池/其他方法?
我ThreadLocal在我的网络应用程序中填充了几个.而且,在远程调试webapp时,我希望看到这些ThreadLocal变量的值Eclipse(就像Eclipse在Debug透视图中的变量选项卡中显示其他变量一样).
任何想法如何ThreadLocal在调试过程中查看变量的值Eclipse?
谢谢!
所以Visual Studio支持的c ++ 11功能列表.
thread_local支持被标记为partial.我无法找到这里究竟部分支持意味着什么的解释.他们只是别名__declspec(线程)吗?
我可以使用boost :: thread_specific_ptr,但似乎有一些报告说boost :: thread_specific_ptr很慢.这可能是也可能不是.
具体来说,我希望在最新的linux + gcc和windows + msvc上使用x86/x64上的快速TLS.快速意义在可能的情况下没有系统调用(我认为这对于上述平台是可行的.)
我需要在Java中构建一个工作池,每个工作者都有自己的连接套接字; 当工作线程运行时,它使用套接字但保持打开以便以后重用.我们决定使用这种方法,因为与临时创建,连接和销毁套接字相关的开销需要太多的开销,所以我们需要一种方法,通过这种方法,工作池预先初始化了它们的套接字连接,准备好在保持套接字资源不受其他线程影响的同时承担工作(套接字不是线程安全的),所以我们需要这些内容......
public class SocketTask implements Runnable {
Socket socket;
public SocketTask(){
//create + connect socket here
}
public void run(){
//use socket here
}
Run Code Online (Sandbox Code Playgroud)
}
在应用程序启动时,我们想要初始化工作程序,并希望套接字连接在某种程度上......
MyWorkerPool pool = new MyWorkerPool();
for( int i = 0; i < 100; i++)
pool.addWorker( new WorkerThread());
Run Code Online (Sandbox Code Playgroud)
当应用程序请求工作时,我们将任务发送到工作池以立即执行...
pool.queueWork( new SocketTask(..));
Run Code Online (Sandbox Code Playgroud)
更新了工作代码
根据Gray和jontejj的有用评论,我有以下代码工作...
SocketTask
public class SocketTask implements Runnable {
private String workDetails;
private static final ThreadLocal<Socket> threadLocal =
new ThreadLocal<Socket>(){
@Override
protected Socket initialValue(){
return new Socket();
}
};
public SocketTask(String details){ …Run Code Online (Sandbox Code Playgroud) 后台工作线程是否重复使用?
具体来说,如果我在后台工作程序的DoWork()方法中设置了一个命名数据槽(线程局部存储),那么该数据槽的值是否会持续存在,以后可能会发现某个其他线程?
我不会这么想,但我有这个错误......
编辑:这篇博客文章表明BackGroundWorker使用ThreadPool,这意味着线程被重用.所以问题就变成了; 重用线程是否可能在调用之间持久保存线程本地存储?
c ++编译器如何在C++ 0x中实现线程本地存储
我在谷歌搜索过这个.但我找不到任何关于此事的内容.
有没有人对此有任何材料?
使我无法定期使用Akka(使用Java)是我对使用ThreadLocals的库的一个担忧.
这就是我认为一些Akka Dispatchers可能会导致ThreadLocal变量被遗忘,或者一起丢失.所以显而易见的解决方案是避免使用ThreadLocals但是有很多库使用它们:Spring,Wro4j,Log4j等等......
ThreadLocals通常在Servlet容器中正常工作.这是因为即使容器有线程池,请求主要是一个同步生命周期,因此通常在请求结束时,像Spring和Wro4J这样的东西会清除那里的threadlocals.像Tomcat这样的容器甚至可以监控线程局部泄漏.
根据我的理解,Akka的情况并非如此.
人们如何解决这个问题?
现在我只是避免使用Akka并使用Message Queue(如RabbitMQ或ActiveMQ),但想使用Akka,因为它涵盖了更广泛的异步问题/解决方案.
我也认为Netty有类似的问题,但我相信Netty提供了一些可用于代替ThreadLocal的Channel Context对象,理论上一些库可能知道使用它而不是ThreadLocal.
thread-local ×10
java ×6
c++ ×3
c++11 ×2
.net ×1
akka ×1
boost-thread ×1
c# ×1
caching ×1
concurrency ×1
debugging ×1
eclipse ×1
java-8 ×1
java-stream ×1
threadpool ×1
visual-c++ ×1