发布对象和线程安全

时间:2012-02-25 21:28:42

标签: java thread-safety

我在“Java Concurrency In Practice”中读到“在完全构造之前发布对象会损害线程安全性”。 有人可以解释一下吗?

2 个答案:

答案 0 :(得分:3)

考虑以下代码:

public class World{
    public static Point _point;

    public static void main(String[] args){
        new PointMaker().start();
        System.out.println(_point);
    }
}

public class Point{
    private final int _x, _y;

    public Point(int x, int y){
        _x = x;
        World._point = this;//BAD: publish myself before I'm fully constructed
        //some long computation here
        _y = y;
    }

    public void toString(){
        return _x + "," + _y;
    }
}

public class PointMaker extends Thread{
    public void run(){
        new Point(1, 1);
    }
}

由于Point在设置_y的值之前发布了自己,因此对println的调用可能会产生"1,0"而不是预期的"1,1"

(请注意,如果"null" + PointMaker在调用{{1}之前未能设置Point.<init>字段,则可能会产生World._point执行。)

答案 1 :(得分:1)

允许new运算符在类的构造函数完成之前返回一个值。因此,变量可能不会读取null但包含未初始化的类实例。这是由于字节重新排序而发生的。

一些澄清: 从单线程的角度来看,允许JVM重新排序某些指令。在传统上创建实例时,您会认为它是这样的:

  • 分配内存
  • 运行初始化(构造函数)
  • 指定参考 VAR

实际上JVM可能会执行以下操作:

  • 分配内存
  • 指定对var
  • 的引用
  • 运行初始化 (构造)

这具有性能优势,因为地址不需要再次查找。从单线程的角度来看,这不会改变逻辑的顺序。你的程序运行正常。但这在多线程代码中造成了问题。这意味着可以在构造函数运行之前发布引用。因此,您需要“先发生”规则以确保实例已完全初始化。声明变量volatile dos强制执行此类发生之前的规则。

有关重新排序的更多信息: http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#reordering