同步Getter方法能否看到完全或部分初始化对象?同步方法是否存在字段部分初始化?
问题解答
问题1:同步的Getter方法能否看到完全初始化或部分初始化的对象?
- 分两种场景判断:
- 对象正确发布时:同步Getter可以保证看到完全初始化的对象。Java内存模型中,
synchronized的锁操作遵循happens-before规则——若线程A释放某对象的锁,线程B随后获取同一对象的锁,那么线程A的所有操作(包括对象构造时的字段赋值)都会对线程B可见。只要对象是在构造完成后,通过同步机制、volatile变量、静态初始化等正确方式对外暴露,同步Getter就能读到完整的初始化值。 - 对象不正确发布时:同步Getter仍可能看到部分初始化的对象。比如构造函数中提前把
this引用逸出(例如赋值给全局变量),或者未同步地将对象引用赋值给共享变量,此时其他线程可能在构造函数未执行完毕时就拿到对象引用并调用同步Getter。由于构造过程没有持有对象锁,后续Getter获取锁的动作与构造时的字段赋值之间没有happens-before关系,就可能读到未完全初始化的字段值。
- 对象正确发布时:同步Getter可以保证看到完全初始化的对象。Java内存模型中,
问题2:假设我有如下Java类:
class Point { int x, y, z; public Point(int x, int y, int z) { this.x = x; this.y = y; this.z = z; } public synchronized int getSum() { return x + y + z; } }
请问在该类的synchronized修饰的getSum()方法中,是否可能出现字段部分初始化的情况?
答案取决于对象的发布方式:
- 如果对象是不正确发布的(比如直接将对象引用赋值给未加同步的共享变量),那么getSum()确实可能读到部分初始化的字段。例如线程A执行
p = new Point(1,2,3)时,由于指令重排,可能在构造函数还没完成所有字段赋值时就把对象引用赋值给p;线程B随后调用p.getSum(),虽然getSum是同步方法,但构造函数的字段赋值和线程B的读操作之间没有happens-before关系,就可能读到x、y、z的默认值(0)或者部分赋值后的结果(比如x=1,y=0,z=0)。 - 如果对象是正确发布的(比如通过同步方法/块赋值给共享变量、用
volatile修饰共享变量、静态初始化创建对象等),那么getSum()就能保证读到完全初始化的字段值,不会出现部分初始化的情况。
内容的提问来源于stack exchange,提问作者Sincerely
相关产品推荐
相关产品推荐

