java - 同步的、易变的和线程安全的

标签 java multithreading synchronization thread-safety volatile

我最近在看一些关于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;
  }
}

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

最佳答案

简答:

是的,但在最后一种情况下您甚至不需要 synchronizedsetValue 做的唯一一件事是一个单个 操作,一个写入——并且在每个操作中,volatiles 都是原子的。也就是说,每次写入都是原子的,每次读取也是原子的。

更长的答案:

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

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

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

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

关于java - 同步的、易变的和线程安全的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19827180/

相关文章:

java - 一旦变量设置为 false,如何停止线程?

arrays - 在并行化期间在 Chapel 中使用同步变量时遇到问题

go - 如何跨 goroutines 共享 map

java - 在 Intent 之间发送自定义模型列表

java - 访问存储在java中内存位置的数据

java - 如何处理JSP下拉列表错误处理

c# - IOCP 是在 I/O 发生时或之后运行的线程吗?

java - Mybatis中如何映射多个bean?

java - 避免 Iterator ConcurrentModificationException 的方法

java - 随时将变量传递到线程中?