考虑一个表示简单单元格的类:
class Cell {
private int x;
Cell(int x) {
this.x = x;
}
int getX() {
return x;
}
void setX(int x) {
this.x = x;
}
}
如果我想使其线程安全,我应该只同步方法还是也同步构造函数?
class Cell {
private int x;
Cell(int x) {
synchronized(this) { // <- Is this synchronization necessary?
this.x = x;
}
}
synchronized int getX() {
return x;
}
synchronized void setX(int x) {
this.x = x;
}
}
如果是,为什么java.util.Vector
构造函数中没有synchronized
块?
根据JLS#8.8.3
实际上不需要对构造函数进行同步,因为它会锁定正在构建的对象,通常直到对象已经完成了他们的工作。
因此,这意味着引用是在可访问之前同步的。
由于您正在正确同步,因此可以说,只要同步在所述字段上是一致的,那么构造函数中发生的写入将在对象发布之前发生
在您的情况下,由于getX()
和setX()
都是同步的,因此同步是一致的,您不需要在构造函数中同步。
现在,您是否需要在构造函数上使用synchronize(this)
否,正如JLS所提到的,它在您不知情的情况下隐式同步。
这带来了一个重要的切点-您永远不应该允许对对象的引用从其构造函数中转义,因为这将允许访问部分构造的对象。
因此,假设您遵循这条编程的铁定律,您永远不需要同步构造函数,因为在构造函数返回之前,任何方法,无论是并发方法还是其他方法,都无法访问对象。因此,同步毫无逻辑意义。
不,您不需要同步构造函数,因为只要您在构造函数中,引用就不会泄露到另一个线程。
除此之外:
- 考虑让你的类不可变。然后不需要同步
- 我宁愿不做一个像synchronzid这样的简单类,而是把同步留给类的用户。如果只从一个线程访问同一个对象,则不需要同步
您的情况不需要。
- 如果您正在访问可共享数据,则需要使用sych块进行包装
在您的情况下,您正在访问实例变量,由于您在构造函数中,该变量只能由一个线程访问/可见。您将调用构造函数,分配内存,方法调用结束时返回调用方法的唯一指针,这样指针/实例对于线程是唯一的。
一旦收到指针。如果指针/对象变量像这样在多个线程中共享。
Object obj = new Object();
Thread t1 = new Thread(new x(obj));
Thread t2 = new Thread(new x(obj));
Class x implements runnable{
Object obj;
x (Object obj){
this.obj = obj;
}
run() {
// do some operation on Obj -
}
}
}
在这种情况下,obj在多个线程之间共享,因此您的实例变量x
受竞赛条件约束。因此,您必须确保使用其他方法。
这就是为什么向量构造函数并没有sycn块,而其他方法却有。
假设这个类中有一个静态变量。则需要按同步块进行包装,因为任何一个同步块都可以访问它。
构造函数永远不应该同步,因为我想这是不实用的。(你什么时候需要同时从不同的地方调用同一个构造函数?)
以下是进一步的信息: