【问题标题】:Why do I get inconsistencies in this method when running为什么我在运行时会出现这种方法不一致的情况
【发布时间】:2016-11-17 12:01:58
【问题描述】:

有时,当运行时变量总数将等于其他值而不是 50005000 它总是像 50005001 一样短,有时运行时为什么会发生这种情况不应该同步(这个)创建一个锁,只有在锁被释放后才能更新线程?

import java.util.concurrent.atomic.AtomicLong;

public class CurrentThread {
    public static AtomicLong c = new AtomicLong(0L);
    public static AtomicLong total = new AtomicLong(0L);
    public static void main(String[] args) {

        Thread t = Thread.currentThread();
        System.out.println(t);
        t.setName("My Thread");
        System.out.println(t);

        for (int x = 0; x < 10; x++) {
            System.out.println("Instance " + x);
            new Thread(new Updater(x, "Thread: " + String.valueOf(x))).start();
        }
        try {
            Thread.sleep(1000);
        }
        catch (InterruptedException e) {
        }    
    }
}
class Updater implements Runnable {
    public int na;   
    private String threadName;  
    public Updater(int n, String threadName) {
        this.na = n;
        this.threadName = threadName;  
    }  
    @Override
    public void run() {
        this.updateCount();
        if(CurrentThread.total.get() == 50005000) {
            System.out.println("Passed");
        }
        else {
            System.out.println("Failed");
        }
    }    
    public  void  updateCount() {

            while (CurrentThread.c.get() < 10000) {
                synchronized(this) {
                    CurrentThread.c.getAndIncrement();
                    CurrentThread.total.addAndGet(CurrentThread.c.get());
                    System.out.println(this.threadName + " " + String.valueOf(CurrentThread.c.get()) + " " + CurrentThread.total.get() + " " + System.nanoTime());
                }
            }
      }  
}

【问题讨论】:

    标签: java concurrency java.util.concurrent


    【解决方案1】:

    您在this 上进行同步,实际上根本没有同步,因为每个线程都有不同的Runnable 实例。

    改为同步所有Updater 实例之间共享的内容,例如Updater.class.


    但是,请注意,在 AtomicLong 上进行同步有点代码味道 - 它意味着已经以原子方式执行操作。

    您可以改用compareAndSet,并避免完全同步,例如:

    while (CurrentThread.c.get() < 10000) {
      while (true) {
        long currValue = CurrentThread.c.get();
        if (currValue >= 10000) break;
    
        long newValue = currValue + 1;
    
        // Only sets c to newValue if its value is still currValue.
        if (CurrentThread.c.compareAndSet(currValue, newValue)) {
          long total = CurrentThread.total.addAndGet(newValue);
          System.out.println(
              this.threadName + " " + newValue + " " + total + " " + System.nanoTime());
          break;
        }
      }
    }
    

    请注意,这会使用“已知”值,例如 newValuetotal,而不是从 AtomicLong 再次获取它们。

    【讨论】:

    • 使用同步修饰符的正确位置在哪里?目前正在阅读 Java Concurrency in Practice 一书。
    • 这是一个非常广泛的问题 - 无论您需要原子地执行多个操作,并且相对于其他线程相互排斥。关键是您在这里不需要它,因为AtomicLong 提供了一种替代(和更轻量)的方法。
    • 这里不需要任何同步,因为您共享的public static AtomicLong total 提供了原子操作。 (我没有使用过AtomicLong,但它应该按照命名的方式运行)
    • @sura2k 仅使用AtomicLong 并不能保证原子更新——尤其是因为AtomicLong 的两个实例之间存在更新。您可以原子地执行此操作 - 如上所述 - 您只需要非常小心。
    • @Andy - 我的错,我只看到了一个。如果第一个线程在 if (CurrentThread.c.compareAndSet(currValue, newValue)) { 之后空闲并且第二个线程同时执行 c.compareAndSettotal.addAndGet 会发生什么,然后线程 1 返回并执行 total.addAndGet ?会好吗?
    猜你喜欢
    • 1970-01-01
    • 2021-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-05
    相关资源
    最近更新 更多