跨线程与 ThreadLocal 中的对象交互
我首先要说的是,这是一个坏主意。ThreadLocal
is a 特殊班 https://stackoverflow.com/a/15653015/113632提供速度和线程安全的优势如果使用正确的话。尝试与 a 跨线程通信ThreadLocal
首先就违背了使用该类的目的。
如果您需要跨多个线程访问对象,则可以使用为此目的设计的工具,特别是线程安全集合java.util.collect.concurrent https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/package-summary.html例如ConcurrentHashMap http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ConcurrentHashMap.html,您可以用它来复制ThreadLocal
通过使用Thread
对象作为键,如下所示:
ConcurrentHashMap<Thread, AtomicBoolean> map = new ConcurrentHashMap<>();
// pass map to threads, let them do work, using Thread.currentThread() as the key
// Update all known thread's flags
for(AtomicBoolean b : map.values()) {
b.set(true);
}
更清晰,更简洁,避免使用ThreadLocal
在某种程度上它根本不是设计的目的。
通知线程其数据已过时
我只需要一种方法来通知所有这些线程它们的 ThreadLocal 变量已过时。
如果您的目标只是通知其他线程某些内容已更改,则不需要ThreadLocal
根本不。只需使用一个AtomicBoolean
并与您的所有任务共享它,就像您共享您的任务一样ThreadLocal<AtomicBoolean>
。顾名思义,更新为AtomicBoolean
是原子且可见的跨线程。更好的是使用真正的同步辅助工具,例如CyclicBarrier https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CyclicBarrier.html or Phaser https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Phaser.html,但对于简单的用例来说,仅使用AtomicBoolean
.
创建一个可更新的“ThreadLocal
"
综上所述,如果你really想要实现一个全局可更新的ThreadLocal
你的实现被破坏了。事实上,您没有遇到问题只是巧合,未来的重构很可能会引入难以诊断的错误或崩溃。那个它“工作顺利“仅意味着您的测试不完整。
- 首先也是最重要的,一个
ArrayList
不是线程安全的。当多个线程可能与它交互时,您根本无法使用它(没有外部同步),即使它们会在不同的时间这样做。您现在没有看到任何问题只是巧合。
- 将对象存储为
List
阻止我们删除陈旧的值。如果你打电话ThreadLocal.set()
它将追加到您的列表中,而不删除以前的值,如果您预计一旦线程终止这些对象将变得无法访问,这会导致内存泄漏和潜在的意外副作用,通常是这样的情况ThreadLocal
实例。您的用例巧合地避免了这个问题,但仍然不需要使用List
.
这是一个实现IterableThreadLocal
它安全地存储和更新所有现有的实例ThreadLocal
的值,适用于您选择使用的任何类型:
import java.util.Iterator;
import java.util.concurrent.ConcurrentMap;
import com.google.common.collect.MapMaker;
/**
* Class extends ThreadLocal to enable user to iterate over all objects
* held by the ThreadLocal instance. Note that this is inherently not
* thread-safe, and violates both the contract of ThreadLocal and much
* of the benefit of using a ThreadLocal object. This class incurs all
* the overhead of a ConcurrentHashMap, perhaps you would prefer to
* simply use a ConcurrentHashMap directly instead?
*
* If you do really want to use this class, be wary of its iterator.
* While it is as threadsafe as ConcurrentHashMap's iterator, it cannot
* guarantee that all existing objects in the ThreadLocal are available
* to the iterator, and it cannot prevent you from doing dangerous
* things with the returned values. If the returned values are not
* properly thread-safe, you will introduce issues.
*/
public class IterableThreadLocal<T> extends ThreadLocal<T>
implements Iterable<T> {
private final ConcurrentMap<Thread,T> map;
public IterableThreadLocal() {
map = new MapMaker().weakKeys().makeMap();
}
@Override
public T get() {
T val = super.get();
map.putIfAbsent(Thread.currentThread(), val);
return val;
}
@Override
public void set(T value) {
map.put(Thread.currentThread(), value);
super.set(value);
}
/**
* Note that this method fundamentally violates the contract of
* ThreadLocal, and exposes all objects to the calling thread.
* Use with extreme caution, and preferably only when you know
* no other threads will be modifying / using their ThreadLocal
* references anymore.
*/
@Override
public Iterator<T> iterator() {
return map.values().iterator();
}
}
正如您希望看到的那样,这只不过是一个包装器ConcurrentHashMap
,并产生与直接使用相同的开销,但隐藏在 a 的实现中ThreadLocal
,用户通常期望它是快速且线程安全的。我出于演示目的实现了它,但我真的不建议在任何设置中使用它。