在Brian Goetz的Java Concurrency In Practice中,有以下示例(清单4.11缩短)。
public class SafePoint {
private int x, y;
public SafePoint(int x, int y) {
this.x = x;
this.y = y;
}
public synchronized int[] get() {
return new int[] { x, y };
}
public synchronized void set(int x, int y) {
this.x = x;
this.y = y;
}
}它真的是线程安全的吗?x和y既不是volatile也不是final,它们是在没有锁定this的情况下设置的,这意味着调用get()的另一个线程可能会看到陈旧的值(零)。我是不是漏掉了什么?
发布于 2015-07-15 19:00:16
正如synchronized所指出的那样,该方法是线程保存的。至于您的属性x和y,在您的示例中,这些属性只在构造函数中编写。因此,您不需要将它们设置为synchronized。如果您为x和/或y实现了某种设置器,则需要将这些设置器设为synchronized,并将属性设为volatile。
发布于 2015-07-16 21:59:12
@jameslarge你看过标题了吗?
好的,我在你正在阅读的这本书的索引中查找了“安全发布”,这是3.5.3节中的内容:
正确构造的对象可以通过以下方式安全地发布:从
static初始化器初始化对象引用,将对它的引用存储到volatile字段或AtomicReference中,将对它的引用存储到正确构造对象的final字段中,或者将对它的引用存储到由锁正确保护的字段中。
因此,答案取决于如何使用SafePoint类。
如果线程A构造一个SafePoint(5, 15)实例,然后将其存储在非final、非volatile、非static字段中,然后线程B调用safePoint.get();返回给线程B的值可以是[0, 0],也可以是[5, 0]、[0, 15]或[5, 15]。
如果safePoint字段为final或volatile,或者它是由静态初始化器设置的static字段,则返回给线程B的值将始终为[5, 15]。
这个示例在我的环境中打印result = [5, 15],但是如果我对Goetz先生的书的理解是正确的,那么JLS允许它返回其他三种可能性中的任何一种。
public class SafePointDemo {
SafePoint safePoint;
void threadAwork() {
safePoint = new SafePoint(5, 15);
sleep(15000);
}
void threadBwork() {
sleep(10000);
int[] result = safePoint.get();
System.out.println("result = [" + result[0] + ", " + result[1] + "]");
}
private void sleep(long n) {
try {
Thread.sleep(n);
} catch(InterruptedException ex) {
//do nothing
}
}
class SafePoint {
private int x, y;
public SafePoint(int x, int y) {
this.x = x;
this.y = y;
}
public synchronized int[] get() {
return new int[] { x, y };
}
public synchronized void set(int x, int y) {
this.x = x;
this.y = y;
}
}
public static void main(String[] args) {
SafePointDemo safePointDemo = new SafePointDemo();
Thread threadA = new Thread(() -> safePointDemo.threadAwork());
Thread threadB = new Thread(() -> safePointDemo.threadBwork());
threadA.start();
threadB.start();
}
}发布于 2015-07-15 19:00:27
虽然整数不是最终的,但通过在构造函数中只给它们赋值,就可以有效地使它们成为最终的,因为在设置整数之前,指向对象的指针对调用者不可用。
get方法的同步只保证一次只有一个线程能够执行get方法,任何其他线程都将被挂起,直到第一个线程完成。
https://stackoverflow.com/questions/31428343
复制相似问题