7

我最近正在阅读一些有关 Java 并发的书籍。关于线程安全,如果无法使类不可变,则始终可以通过同步其数据来确保线程安全。

下面的类显然不是线程安全的

public class NotThreadSafe {
  private int value;

  public void setValue(int value) {
    this.value = value;
  }

  public int getValue() {
    return this.value;
  }
}

然后我可以同步写入,但它仍然不是线程安全的

public class StillNotThreadSafe {
  private int value;

  public synchronized void setValue(int value) {
    this.value = value;
  }

  public int getValue() {
    return this.value;
  }
}

因为我不仅需要同步写入,还需要同步读取

public class ThreadSafe {
  private int value;

  public synchronized void setValue(int value) {
    this.value = value;
  }

  public synchronized int getValue() {
    return this.value;
  }
}

现在的问题是,通过使用 volatile 我可以保证其他线程会看到更新的值,所以这让我认为这个类应该是线程安全的

public class NotSure {
  private volatile int value;

  public synchronized void setValue(int value) {
    this.value = value;
  }

  public int getValue() {
    return this.value;
  }
}

最后一个类是线程安全的吗?

4

2 回答 2

6

简短的回答:

是的,但在最后一种情况下你甚至不需要synchronized。唯一要做的setValue就是一个单一的操作,一个写——而易失性在每个操作中都是原子的。也就是说,每次写入都是原子的,每次读取都是原子的。

更长的答案:

当然,如果您要尝试使用以下模式增加值:

NotSure ns = new NotSure();
int v = ns.getValue();
ns.setValue(v + 1);

...那么这不是线程安全的,因为它涉及两个操作ns.value(一个读取和一个写入),而volatile只为您提供一个操作的原子性。synchronized在这种情况下,即使同时添加getter和 setter 也是不够的,因为可以在两个方法调用之间注入操作。

最后一点实际上与“您始终可以通过同步[对象的]数据来确保线程安全”的说法有点相反。如果您想要一种线程安全的增量方式NotSure.value,您需要同步整个增量操作,而不仅仅是访问对象的数据。在这种情况下,您需要同步设置器,否则它可能会在增量方法的操作之间插入自身。getter 仍然不需要同步,因为volatile关键字将确保 getter 获得预递增或后递增的值。

于 2013-11-07T03:00:47.260 回答
0

确实没有特别需要使方法 getValue 或 setValue 同步。

我建议将变量值设为同步。

可以同时进行 getter 或 setter 调用。如果从两个单独的线程调用(一个执行 getter 调用,另一个执行 setter 调用。

使正在访问的对象或数据我们可以使它成为线程安全的。

于 2013-11-07T03:19:28.597 回答