Java 在构造函数中进行同步,使其在
我有一个关于如何通过Java内存模型保证对象是线程安全的问题 我读过很多书,说在构造函数中编写同步作用域没有意义,但为什么没有呢?是的,只要构造中的对象不在线程之间共享(不应该共享),除了构造线程之外,没有其他线程可以访问任何同步(this){…},因此不需要在构造函数中设置该作用域来排除它们。但是同步作用域不仅仅是为了排除;它们还用于创建发生在关系之前的事件 下面是一个示例代码来说明我的观点Java 在构造函数中进行同步,使其在,java,multithreading,concurrency,synchronization,Java,Multithreading,Concurrency,Synchronization,我有一个关于如何通过Java内存模型保证对象是线程安全的问题 我读过很多书,说在构造函数中编写同步作用域没有意义,但为什么没有呢?是的,只要构造中的对象不在线程之间共享(不应该共享),除了构造线程之外,没有其他线程可以访问任何同步(this){…},因此不需要在构造函数中设置该作用域来排除它们。但是同步作用域不仅仅是为了排除;它们还用于创建发生在关系之前的事件 下面是一个示例代码来说明我的观点 public class Counter{ private int count;
public class Counter{
private int count;
public Counter(int init_value){
//synchronized(this){
this.count = init_value;
//}
}
public synchronized int getValue(){
return count;
}
public synchronized void addValue(){
count++;
}
}
考虑这样一种情况,一个线程t0创建一个计数器对象,另一个线程t1使用它。如果构造函数中有synchronized语句,显然可以保证它是线程安全的。(因为同步作用域中的所有动作都有一个“先发生后发生”的关系。)但如果没有,即没有同步语句,Java内存模型是否仍能保证t1可以看到计数t0的初始化写入?我想不是。这就像f.y可以在中的示例代码17.5-1中看到0一样。与JSL.17.5-1的情况不同,现在第二个线程只从同步方法访问字段,但我认为同步语句在这种情况下没有保证效果。(他们不会通过t0创建与任何操作的任何“发生在之前”关系。)有人说,关于构造函数末尾的“发生在边缘之前”的规则保证了这一点,但该规则似乎只是说构造函数发生在finalize()之前 那么我是否应该在构造函数中编写synchronized语句以使对象线程安全?或者,关于Java内存模型,是否有一些规则或逻辑我已经错过了,但实际上没有必要这样做?如果我是真的,那么即使是openjdk的哈希表(尽管我知道它已经过时)似乎也不是线程安全的
还是我对线程安全的定义和并发策略的理解有误?如果我以线程安全的方式(例如通过易失性变量)将计数器对象从t0传输到t1,那么似乎没有问题。(在这种情况下,t0的构造发生在volatile write之前,volatile write发生在t1读取之前,volatile read发生在t1对其执行的所有操作之前。)我是否应该始终在线程之间传输线程安全的对象(但不是不可变的),通过一种导致“发生在之前”关系的方式?如果对象被安全发布(例如,将其实例化为
someVolatileField=new Foo()
),则不需要构造函数中的同步。如果不需要,则构造函数中的同步是不够的
关于这一点,java并发兴趣列表上有一个较长的讨论;我将在这里提供摘要(完整披露:我开始了讨论,并参与了整个讨论)
记住,before边只在一个线程释放锁和后续线程获取锁之间应用。因此,假设您有:
someNonVolatileField = new Foo();
这里有三组重要的行动:
someNonVolatileField
synchronized doFoo()
方法。现在我们再添加两个操作:
someNonVolatileField
参考doFoo()
,其中包括获取和释放对象的监视器someNonVolatileField
someNonVolatileField
参考doFoo()
,其中包括获取和释放对象的监视器doFoo()
的调用正式发生在构造函数之前
这确实给您带来了一点好处;这意味着任何同步方法(或块)保证看到构造函数的全部效果,或者没有任何效果;它不会只看到构造函数的一部分。但在实践中,您可能希望保证看到构造函数的效果;这就是您编写构造函数的原因
您可以通过让doFoo()不同步来解决这个问题,而是设置一些自旋循环,等待一个表示构造函数已运行的标志,然后是一个手动
synchronized(this)
块。但是当您达到这个复杂度时,最好只说“假设该对象的初始发布是安全的,则该对象是线程安全的。”这是大多数可变类的事实假设,这些可变类自称是线程安全的;不可变类可以使用final
字段,即使在不安全发布的情况下,这些字段也是线程安全的,但不需要显式同步。”考虑这样一种情况,一个线程t0创建一个计数器对象,另一个线程t1使用它。“在构造函数返回t0之前,t1如何引用计数器
?安全发布解决了您的问题。@Real怀疑论者假设JVM为1.5+,在第一个线程写入引用和任何后续线程读取引用之间有一个完整的before edge。as