螺纹限制

我正在阅读Java并发实践,并且与线程限制概念有点混淆。书中说:

当对象被限制在线程中时,即使被限制的对象本身不是线程安全的,这种用法也自动是线程安全的

那么当一个对象被限制在一个线程中时,没有其他线程可以访问它吗?这就是被限制在线程中的含义吗?如何将对象限制在线程中?

编辑:但是,如果我仍然想与另一个线程共享该对象,该怎么办?假设线程 A 完成对象 O 后,线程 B 想要访问 O。在这种情况下,在A完成之后,O还能被限制在B中吗?

使用局部变量肯定是一个例子,但这只是意味着你不与其他线程共享你的对象(完全)。在JDBC连接池的情况下,一旦线程完成连接,它就不会将一个连接从一个线程传递到另一个线程(对此完全无能为力,因为我从未使用过JDBC)。


答案 1

那么当一个对象被限制在一个线程中时,没有其他线程可以访问它吗?

不,情况恰恰相反:如果您确保没有其他线程可以访问某个对象,则该对象被限制为单个线程。

没有语言或 JVM 级别的机制可以将对象限制为单个线程。您只需要确保对对象的引用不会转义到另一个线程可以访问的位置。有一些工具可以帮助避免泄漏引用,例如 ThreadLocal 类,但没有一些工具可以确保在任何地方都不会泄漏任何引用。

例如:如果对对象的唯一引用来自局部变量,则该对象肯定仅限于单个线程,因为其他线程永远无法访问局部变量。

同样,如果对一个对象的唯一引用来自另一个对象,该对象已被证明被限制为单个线程,则该第一个对象被限制在同一线程中。

广告编辑:在实践中,您可以有一个对象,该对象在其生存期内一次只能由单个线程访问,但该单个线程会更改(来自连接池的 JDBC 对象就是一个很好的例子)。Connection

但是,证明这样的对象只能由单个线程访问,这比为在其整个生命周期中仅限于单个线程的对象证明它要困难得多。

在我看来,这些对象从来没有真正“局限于单个线程”(这意味着一个强有力的保证),但可以说“一次只能由单个线程使用”。


答案 2

最明显的例子是使用线程本地存储。请参阅下面的示例:

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();
        }
    }
}

对象本身可以与其他线程“共享”,因为如果您将其他线程而不是在运行时传递给映射的方法,您将获得属于其他线程的对象。但你选择不这样做。这是“仅限于线程的用法”。换句话说,运行时条件使得对象实际上永远不会在不同线程之间共享。UnsafeStuffThread.currentThread()get

另一方面,在下面的示例中,对象自动限制在线程中,也就是说,“对象本身”被限制在线程中。从某种意义上说,无论运行时条件如何,都不可能从其他线程获取引用:

class SomeClass {
    void calledByMultipleThreads(){
        UnsafeStuff mystuff = new UnsafeStuff();
        mystuff.modifySomeStuff();
        System.out.println(mystuff.toString());
    }
}

此处,在方法内分配,并在方法返回时超出范围。换句话说,Java 规范静态地确保对象始终局限于一个线程。因此,确保限制的不是运行时条件或使用它的方式,而是Java规范。UnsafeStuff

事实上,现代JVM有时会在堆栈上分配这样的对象,这与第一个例子不同(没有亲自检查过,但我认为至少当前的JVM没有这样做)。

换句话说,在第一个例子中,JVM不能通过查看内部来确定对象是否被限制在线程内(谁知道其他方法正在搞砸什么)。在后一个例子中,它可以。calledByMultipleThreads()SomeClass.map


编辑:但是,如果我仍然想与另一个线程共享对象怎么办?假设线程 A 完成对象 O 后,线程 B 想要访问 O。在这种情况下,在A完成之后,O还能被限制在B中吗?

我不认为在这种情况下它被称为“受限”。执行此操作时,您只是确保不会同时访问对象。这就是 EJB 并发的工作原理。您仍然必须将有问题的共享对象“安全地发布”到线程。


推荐