【问题标题】:Thread safe vs. non thread safe implementations of a counter计数器的线程安全与非线程安全实现
【发布时间】:2016-11-19 06:56:53
【问题描述】:

这是一项学校作业,但我真的需要一些帮助。我一辈子都想不通为什么nextValue() 的两个不同版本的行为不同——一个是线程安全的,另一个不是。至少有人可以给我一些正确方向的指示吗?

我在下面的同一个类中包含了这两个版本,但显然它们并没有同时出现在代码中......

public class NumGenerator {

    static final int MIN_VALUE = -256;
    static final int MAX_VALUE = 255;
    static final int INITIAL_VALUE = MIN_VALUE -1;


    private final AtomicInteger counter = new AtomicInteger(INITIAL_VALUE);
    private final AtomicInteger resetCounter = new AtomicInteger(0); 

    private final Object lock = new Object();

    // Thread safe
    public int nextValue() {
        int next = counter.incrementAndGet();      
        if (next > MAX_VALUE) {                         
            synchronized (lock) {                       
                next = counter.incrementAndGet();   
                if (next> MAX_VALUE) {                  
                    counter.set(MIN_VALUE); 
                    resetCounter.incrementAndGet();
                    next = MIN_VALUE;
                }

            }
        }
        return next;   
    }

     // Non thread safe
     public int nextValue() {
        int next = counter.incrementAndGet();
        if (next > MAX_VALUE) {
            synchronized (lock) {
                int i = counter.get();
                if (i > MAX_VALUE) {
                    counter.set(INITIAL_VALUE);
                    resetCounter.incrementAndGet();
                }
                next = counter.incrementAndGet();
            }
        }
        return next;
    }
}

【问题讨论】:

  • 这两个代码的逻辑似乎不一样。恕我直言,这两个函数都是线程安全的。然而,如前所述,这两个函数都可能返回与预期不同的输出。
  • 你是对的,它们返回不同的输出...但是为什么呢?在我看来,它们在功能上是相同的。我错过了什么?
  • counter.get();counter.incrementAndGet(); 是不同的。所以你会得到不同的输出
  • 好的,但那是在外部 IF 语句内......你的意思是不同的线程可能会出现在 int next = ...synchronized... 之间并搞砸结果?我想是的,但还有第二个 IF 语句?

标签: java multithreading synchronization thread-safety atomic


【解决方案1】:

假设值为:MIN_VALUE = -1,MAX_VALUE = 3,counter = 3。

代码 1:

synchronized (lock) {                       
                next = counter.incrementAndGet();   
                if (next> MAX_VALUE) {                  
                    counter.set(MIN_VALUE); 
                    resetCounter.incrementAndGet();
                    next = MIN_VALUE;
                }

            }
  1. 它增加计数器的值,然后将其用于比较。
  2. 所以 next 的值变成 4。if(next > MAX_VALUE) 变成 if(4>3) 这会改变 next 到 -1 的值并返回它。

代码 2:

synchronized (lock) {
                int i = counter.get();
                if (i > MAX_VALUE) {
                    counter.set(INITIAL_VALUE);
                    resetCounter.incrementAndGet();
                }
                next = counter.incrementAndGet();
            }
  1. 它将值分配给计数器,然后进行比较。
  2. i 的值仍为 3。if(i > MAX_VALUE) 变为 if(3 > 3),而不是返回 3 作为输出。

incrementAndGetget 是不同的。 所以具有相同值的代码返回不同的输出。 一种是先增加值然后检查条件,另一种是先检查值然后执行操作。

即使是 if(variable > MAX_VALUE) 中的代码也会导致不同的输出。

所以这与线程安全无关。

【讨论】:

  • 我刚刚弄清楚有什么区别。你是对的,区别在于使用IncrementAndGet() 与仅使用get(),但我不同意它与线程安全没有任何关系。两个代码都在一个线程中工作,但是当同时调用时,值有可能在第一个 incrementAndGet()get() 之间被另一个线程更改,因此第二个 IF 语句无效,因为它应该等于第一个 IF 语句。基本上,代码应该假定counter 在第二个 IF 处可以是任何东西,就像没有外部 IF 一样。
  • @BadCash 理想情况下不应如此。由于同步块应确保两个代码都是线程安全的。因此increamentAndGet()get() 都将不起作用,因为只有一个线程有机会在块内执行。现在谈到线程安全,在同步块int next = counter.incrementAndGet(); 之外存在一个问题,当 2 个线程同时递增值时,它们的本地副本 next 将具有不同的值。因此,在考虑这一行时,这两个代码都不是线程安全的。
  • 是的,但在第一个版本中,同步块内有一个相同的next = incrementAndGet(); if( next > MAX_VALUE ),使其成为线程安全的。我想这与制作整个方法 synchronized 并且只有一个 IF 语句具有相同的效果,但还有一个额外的好处是,当第一个 IF 语句为 false 时,它​​不会在所有情况下阻塞其他线程的方法...
【解决方案2】:

认为这两种方法在逻辑上都是线程安全的。因为在这两种方法中都使用了同步。同步块用于线程安全。

【讨论】:

    【解决方案3】:

    两者都是线程安全的。也许您可以粘贴您如何测试它们的代码。问题可能就在那里。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-05
      • 2011-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多