2

我正在阅读SynchronizedMethodsOracleDoc,但我无法理解这个概念。它这么说。

警告:在构造将在线程之间共享的对象时,要非常小心,不要对对象的引用过早地“泄漏”。例如,假设您要维护一个名为实例的列表,其中包含类的每个实例。您可能想在构造函数中添加以下行:instances.add(this); 但是在对象的构造完成之前,其他线程可以使用实例来访问对象。

这是什么意思?

它还指出:-

其次,当同步方法退出时,它会自动与任何后续对同一对象的同步方法的调用建立起happens-before关系。

发生在关系之前是什么意思?

有人可以详细说明这两点吗?谢谢。

4

3 回答 3

3

过早泄漏

泄漏对尚未完全创建的对象的引用。

例子:

class Someclass{

   public SomeClass(List list){
        list.add(this); //this is leaked outside before complete creation, constructor is still not complete

        //do some other chores to create this object
   }
}

一些线程2:

listPassedToSomeclass //Thread is using this list and working on something

现在,在从构造函数调用列表上的 add 时,您在this列表中发布了由其他线程共享的引用,现在它甚至可以在构造函数结束之前看到添加到列表中的 this 引用(这意味着对象不在稳定状态且未正确创建)。

由于状态不稳定,线程 2 可能会在使用您的对象时出现各种奇怪的行为。所以避免泄漏参考。

发生在之前

在此处输入图像描述

于 2013-08-09T11:20:19.373 回答
2

第一个问题:

import java.util.LinkedList;
import java.util.List;

public class Example implements Runnable {

    private static LinkedList<ListEntry> objList;

    public static class ListEntry {
        public int var = -1;
        public ListEntry(List<ListEntry> objList, int var) {
            synchronized (objList) {
                objList.add(this);
            }
            try {
                Thread.sleep(2);
            } catch (InterruptedException e) {}
            this.var = var;
        }
    }

    public static void main(String[] args) {
        objList = new LinkedList<>();
        new ListEntry(objList, 1);
        for (int i=0; i < 100; i++) {
            new Thread(new Example()).start();
        }
    }

    public void run() {
        for (int i=0; i < 1000; i++) {
            ListEntry lastEntry;
            synchronized (objList) {
                lastEntry = objList.getLast();
            }
            try {
                Thread.sleep(2);
            } catch (InterruptedException e) {}
            int lastVar = lastEntry.var;
            new ListEntry(objList, lastVar + 1);
            System.out.println(lastVar);
            if (lastVar < 0)
                throw new RuntimeException("lastVar = " + lastVar);
        }
    }
}

如果您运行此代码,则会不时抛出 RuntimeException,因为添加到列表中的最后一个对象尚未完成构造,因此它的 var 实例将具有初始值 -1。这可能是因为您在构造函数中“泄漏”了对象引用 (this)。想不出更好的例子。

第二个问题:同步方法保证它在该方法的任何后续调用发生之前完成(已经发生)。不知道怎么形容更好。

于 2013-08-07T15:19:11.557 回答
1

假设您有实例列表。在您的对象的构造函数中,您添加this到该列表中。

文章说实例在多个线程之间共享。因此,在您添加this时,其他线程能够访问它。但是,由于您将其添加到对象的构造函数中,因此该对象尚未完全实例化(直到它从构造函数返回)。

因此,基本上您将共享对未完全实例化的对象的引用。

对于第二点,发生在关系之前意味着同步对象块保证对访问该数据的其他线程可见,因为它总是在可以获取同步对象时执行。不确定我是否解释得当。

看看这个: http ://docs.oracle.com/javase/tutorial/essential/concurrency/memconsist.html

于 2013-08-07T15:17:50.300 回答