线程限制

den*_*iss 37 java concurrency multithreading thread-safety thread-confinement

我正在阅读Java Concurrency in Practice,并且与线程限制概念相混淆.这本书说的

当一个对象局限于一个线程时,即使受限制的对象本身不是这样,这种用法也是自动线程安全的

因此,当一个对象局限于一个线程时,没有其他线程可以访问它吗?是否意味着被限制在线程中?如何将对象限制在线程中?

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

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

Joa*_*uer 40

因此,当一个对象局限于一个线程时,没有其他线程可以访问它吗?

不,这是另一种方式:如果你确保没有其他线程可以访问一个对象,那么该对象被认为仅限于一个线程.

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

例如:如果对象的唯一引用来自局部变量,那么该对象肯定局限于单个线程,因为其他线程永远不能访问局部变量.

类似地,如果对象的唯一引用来自另一个已被证明局限于单个线程的对象,则该第一个对象被限制在同一个线程中.

广告编辑:实际上,您可以拥有一个对象,该对象在其生命周期内一次只能由一个线程访问,但该单个线程发生更改(Connection来自连接池的JDBC 对象就是一个很好的示例).

然而,证明这样的对象只能由单个线程访问比为一个在其整个生命期内局限于单个线程的对象证明它要困难得多.

在我看来,这些对象永远不会"局限于单个线程"(这意味着强有力的保证),但可以说"一次只能被一个线程使用".

  • @ThreadLocal不确保**引用仅限于单个线程.它只是避免通过此引用泄漏它.您仍然可以通过其他方式轻松泄漏它. (8认同)

Enn*_*oji 10

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

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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

UnsafeStuff自身"可共享"用在这个意义上其他线程对象,如果你想通过一些其他的线程,而不是Thread.currentThread()在运行时地图的get方法,你会得到属于其他线程对象.但你选择不这样做.这是"仅限于线程的用法".换句话说,运行时条件使得对象实际上永远不会在不同线程之间共享.

另一方面,在下面的示例中,对象自动局限于线程,因此,"对象本身"仅限于线程.这是因为无论运行时条件如何,都无法从其他线程获取引用:

class SomeClass {
    void calledByMultipleThreads(){
        UnsafeStuff mystuff = new UnsafeStuff();
        mystuff.modifySomeStuff();
        System.out.println(mystuff.toString());
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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


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

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


Ste*_*n C 6

因此,当一个对象局限于一个线程时,没有其他线程可以访问它吗?

这就是线程限制的含义 - 对象只能被一个线程访问.

是否意味着被限制在线程中?

往上看.

如何将对象限制在线程中?

一般原则是不要将引用放在允许另一个线程看到它的地方.枚举一组确保这一点的规则有点复杂,但(例如)if

  • 你创建一个新对象,和
  • 你永远不会将对象的引用分配给实例或类变量,并且
  • 你永远不会调用一个方法来做参考,
  • 然后该对象将被线程限制.

  • @Martin James - 我的书面文字摆在我面前.如果你在上下文中阅读引用的句子,那一点都不清楚.当然没有任何特殊机制可以实现限制. (2认同)

And*_*s_D 5

我猜这就是想说的.就像在run方法内创建一个对象而不是将引用传递给任何其他实例一样.

简单的例子:

public String s;

public void run() {
  StringBuilder sb = new StringBuilder();
  sb.append("Hello ").append("world");
  s = sb.toString();
}
Run Code Online (Sandbox Code Playgroud)

StringBuilder实例是线程安全的,因为它仅限于线程(执行此run方法)