当前位置: 首页 > 知识库问答 >
问题:

同步、易失性和线程安全

庄实
2023-03-14

最近我在读一些关于java并发的书。关于线程安全,如果不可能使一个类变为inmutable,那么可以通过同步它的数据来确保线程安全。

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

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;
  }
}

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

public class NotSure {
  private volatile int value;

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

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

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

共有2个答案

刁茂才
2023-03-14

如果是,则无需将getValue或setValue方法设置为同步。

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

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

使被访问的对象或数据成为线程安全的。

潘弘扬
2023-03-14

简短回答:

是的,但在最后一种情况下,你甚至不需要同步setValue唯一能做的就是一次操作,一次写操作——每个操作中的挥发分都是原子的。也就是说,每次写入都是原子的,每次读取都是原子的。

更详细的回答:

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

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

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

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

 类似资料:
  • 运行之后(在jdk1.8中),答案不是1000。请告诉我原因。

  • 我是java新手。我有点混淆了线程安全和同步。线程安全意味着一个方法或类实例可以被多个线程同时使用,而不会出现任何问题。其中同步意味着一次只能有一个线程运行。 那么它们是如何相互关联的呢?

  • 我试图更好地理解Java多线程。所以,我知道这句话说只有一个线程可以访问一个对象实例,从而调用该实例的方法(例如,在这种情况下,类 中的减方法。 但是,当你仍然有相同的 会发生什么,但两个线程与不同的 相关联,完全试图调用 的方法?我以为不的消息永远不会被打印出来,但它确实打印出来了。毕竟,仍然只有一个对象实例 ,这是否意味着同步不适用于此示例?

  • 在阅读了这个问题和这个(尤其是第二个答案)之后,我对volatile及其关于记忆障碍的语义感到非常困惑。 在上面的例子中,我们写入一个易失性变量,这会导致一个mitch,这反过来会将所有挂起的存储缓冲区/加载缓冲区刷新到主缓存,使其他缓存行无效。 然而,非易失性字段可以优化并存储在寄存器中,例如?那么,我们如何才能确保给定一个写入易失性变量之前的所有状态变化都是可见的呢?如果我们有1000件东西呢

  • 下面的代码创建了一个新的custom um < code > Thread ,并等待线程结束,直到主线程再次激活。 > < li >我不太明白它是如何工作的。为什么< code > myth read . wait();立即接到电话? < li> 为什么不改用< code>Thread.join()? 公共静态void main(String[] args) {

  • 所以我有一个代码: 所以我将线程添加到我的线程列表中,然后启动这些线程。这是MyThread类: 我想做一个程序来创建线程,将它们添加到列表中,调用它们,但是每个线程都应该等到前一个线程结束它的任务。因此输出应该如下所示: 如何使用实现这一点?我尝试了使用的不同方法,但失败了。