最明显的例子是使用线程本地存储。请参见下面的示例:
class SomeClass {
// This map needs to be thread-safe
private static final Map<Thread,UnsafeStuff> map = new ConcurrentHashMap<>();
void calledByMultipleThreads(){
UnsafeStuff mystuff = map.get(Thread.currentThread());
if (mystuff == null){
map.put(Thread.currentThread(),new UnsafeStuff());
return;
}else{
mystuff.modifySomeStuff();
}
}
}
从UnsafeStuff
某种意义上说,对象本身“可以与其他线程共享”,如果您将一些其他线程而不是Thread.currentThread()
在运行时传递给映射的get
方法,您将获得属于其他线程的对象。但你选择不这样做。这是“仅限于线程的使用”。换句话说,运行时条件使得对象实际上从未在不同线程之间共享。
另一方面,在下面的示例中,对象自动被限制在线程中,也就是说,“对象本身”被限制在线程中。这是从某种意义上说,无论运行时条件如何,都无法从其他线程获取引用:
class SomeClass {
void calledByMultipleThreads(){
UnsafeStuff mystuff = new UnsafeStuff();
mystuff.modifySomeStuff();
System.out.println(mystuff.toString());
}
}
在这里,在UnsafeStuff
方法内分配并在方法返回时超出范围。换句话说,Java 规范静态地确保对象始终被限制在一个线程中。因此,确保限制 的不是运行时条件或使用它的方式,而是 Java 规范。
实际上,现代 JVM 有时会在堆栈上分配此类对象,这与第一个示例不同(没有亲自检查过,但我认为至少当前的 JVM 不会这样做)。
然而换句话说,在第一个示例中,JVM 无法通过仅查看内部来确定对象是否被限制在线程内calledByMultipleThreads()
(谁知道其他方法在弄乱什么SomeClass.map
)。在后一个例子中,它可以。
编辑:但是如果我仍然想与另一个线程共享对象怎么办?假设线程 A 处理完对象 O 后,线程 B 想访问 O。在这种情况下,在 A 处理完 O 之后,O 是否仍被限制在 B 中?
在这种情况下,我不认为它被称为“受限”。当您这样做时,您只是确保不会同时访问一个对象。这就是 EJB 并发的工作原理。您仍然必须将有问题的共享对象“安全地发布”到线程。