【问题标题】:java multi thread access to primitive variablesjava多线程访问原始变量
【发布时间】:2011-05-05 04:13:34
【问题描述】:

我知道从不同的线程同时访问同一个对象而不进行同步通常是一件坏事。但是这个案例呢:

我有多个线程正在运行(考虑两个,ThreadA 和 ThreadB)。我也有这个静态类来计算线程做某事的次数。

public class Counter {
  static private int counter=0;
  static public void incCounter() {
    counter++;
  }
}

如果 ThreadA 和 ThreadB 都调用 Counter.incCounter() 会发生什么?

【问题讨论】:

  • 没什么大不了的,但是计数器的总和不会准确。
  • @irreputable:取决于你认为什么是“灾难性的”。如果某些关键的东西依赖于计数器,它将失败。
  • 为什么从不同的线程访问同一个对象是一件坏事?如果需要,那么它就是必要的。
  • @Steve Kuo - 澄清问题以说明非同步并发访问是什么不好。显然,来自多个线程的正确协调访问不是。
  • 对于轻量级性能计数器,我不使用任何类型的线程安全,因为知道计数器可能会稍微超出,但成本最低。计数器仅用作指示。

标签: java multithreading primitive-types


【解决方案1】:

这不安全。

每个线程都会尝试读取counter,添加一个,然后写回结果。您无法保证这些读取和写入发生的顺序,或者即使结果对每个线程都是可见的。

特别是,一种失败情况是每个线程读取值 0,将其递增到 1,然后将值写回 1。即使在两个线程尝试递增计数器之后,这也会为计数器提供值 1。

考虑改用AtomicInteger.incrementAndGet()。

【讨论】:

  • 呵呵,从来不知道它的存在。 SO的每一天都是上学日。谢谢。
【解决方案2】:

其值为 1 或 2。在此上下文中,静态变量和非静态变量没有区别。

【讨论】:

    【解决方案3】:

    不管是静态对象还是实例:如果你从多个线程中改变它,你就会遇到问题。

    【讨论】:

      【解决方案4】:

      为避免冲突,请使用关键字 synchronized。

      public class Counter {
        static private int counter=0;
        public static synchronized void incCounter() {
            counter++;
        }
      }
      

      这个关键字只允许一个线程调用incCounter()。

      【讨论】:

      • 这可行,但与AtomicInteger相比效率低。
      【解决方案5】:

      Dave 是正确的,但快速解决方法是在该方法描述中添加“同步”关键字;如果多个线程调用该方法,它们将在方法边界处阻塞,直到内部(赢得比赛的)线程增加并存在,然后第二个调用者将进入。

      这很像在 Singleton 类上设计一个好的“getInstance()”方法;您通常希望它同步,这样您就不会出现 2+ 个线程进入方法的情况,所有看到“实例”为空,然后 ALL 创建一个新实例,将其分配给本地成员并返回它.

      在这种情况下,您的线程可能会以对“相同”实例的不同引用结束。所以你同步代码块,只有当它为空时才让第一个线程创建实例,否则总是向所有调用者返回相同的实例。

      if(instance == null) 检查加上返回很便宜;在微秒的数量级上,我相信将来调用 getInstance(或在您的示例中为 incCounter),因此如果需要,无需回避 synchronized 关键字;这就是它的用途。

      话虽如此,如果你不能节省微秒......那么你可能使用了错误的语言:)

      【讨论】:

        猜你喜欢
        • 2013-08-31
        • 1970-01-01
        • 2018-07-03
        • 1970-01-01
        • 1970-01-01
        • 2019-10-25
        • 1970-01-01
        • 2012-07-01
        • 1970-01-01
        相关资源
        最近更新 更多