Java 为什么运行此方法时会出现不一致

Java 为什么运行此方法时会出现不一致,java,concurrency,java.util.concurrent,Java,Concurrency,Java.util.concurrent,有时,当运行时,可变总数将等于其他值,而不是50005000。它总是很短,就像有时运行时的50005001。为什么会发生这种情况?不应该同步(这)创建一个锁,它只能在线程释放锁后更新 import java.util.concurrent.atomic.AtomicLong; public class CurrentThread { public static AtomicLong c = new AtomicLong(0L); public static AtomicLong

有时,当运行时,可变总数将等于其他值,而不是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());
                }
            }
      }  
}
import java.util.concurrent.AtomicLong;
公共类当前线程{
公共静态原子长c=新原子长(0L);
公共静态AtomicLong总计=新AtomicLong(0L);
公共静态void main(字符串[]args){
Thread t=Thread.currentThread();
系统输出打印ln(t);
t、 setName(“我的线程”);
系统输出打印ln(t);
对于(int x=0;x<10;x++){
System.out.println(“实例”+x);
新线程(新更新程序(x,“线程:+String.valueOf(x))).start();
}
试一试{
睡眠(1000);
}
捕捉(中断异常e){
}    
}
}
类更新程序实现可运行{
公共int na;
私有字符串threadName;
公共更新程序(int n,字符串threadName){
这个。na=n;
this.threadName=threadName;
}  
@凌驾
公开募捐{
this.updateCount();
if(CurrentThread.total.get()=50005000){
系统输出打印项次(“通过”);
}
否则{
System.out.println(“失败”);
}
}    
public void updateCount(){
while(CurrentThread.c.get()<10000){
已同步(此){
CurrentThread.c.getAndIncrement();
CurrentThread.total.addAndGet(CurrentThread.c.get());
System.out.println(this.threadName+“”+String.valueOf(CurrentThread.c.get())+“”+CurrentThread.total.get()+“”+System.nanoTime());
}
}
}  
}

您正在
上同步此
,实际上根本不同步,因为每个线程都有一个不同的
可运行
实例

在所有
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;
    }
  }
}
while(CurrentThread.c.get()<10000){
while(true){
long currValue=CurrentThread.c.get();
如果(电流值>=10000)中断;
long newValue=currValue+1;
//仅当c的值仍然为currValue时,才将c设置为newValue。
if(CurrentThread.c.compareAndSet(currValue,newValue)){
long total=CurrentThread.total.addAndGet(newValue);
System.out.println(
this.threadName+“”+newValue+“”+total+“”+System.nanoTime());
打破
}
}
}

请注意,这利用了“已知”值,如
newValue
total
,而不是从
AtomicLong
中再次获取它们,因为每个线程都有一个不同的
可运行的
实例,这实际上根本不同步

在所有
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;
    }
  }
}
while(CurrentThread.c.get()<10000){
while(true){
long currValue=CurrentThread.c.get();
如果(电流值>=10000)中断;
long newValue=currValue+1;
//仅当c的值仍然为currValue时,才将c设置为newValue。
if(CurrentThread.c.compareAndSet(currValue,newValue)){
long total=CurrentThread.total.addAndGet(newValue);
System.out.println(
this.threadName+“”+newValue+“”+total+“”+System.nanoTime());
打破
}
}
}

请注意,这会使用“已知”值,如
newValue
total
,而不是从
AtomicLong

中再次获取它们。使用同步修改器的正确位置在哪里?目前正在阅读《Java并发性实践》一书。这是一个非常广泛的问题——无论您在哪里需要以原子方式执行多个操作,并且与其他线程相互排斥。关键是这里不需要它,因为
AtomicLong
提供了一种替代的(重量更轻的)方法。这里不需要任何同步,因为共享的
public static AtomicLong total
提供了原子操作。(我没有使用过
AtomicLong
,但它应该按照命名的方式运行)@sura2k简单地使用
AtomicLong
并不能保证原子更新,尤其是因为
AtomicLong
的两个实例之间有更新。你可以像上面演示的那样原子化地做,你只需要非常小心。@Andy-我的错,我刚刚看到一个。如果第一个线程在
if(CurrentThread.c.compareAndSet(currValue,newValue))之后空闲,会发生什么情况{
和第二个线程同时执行
c.compareAndSet
total.addAndGet
,然后线程1返回并执行
total.addAndGet
?可以吗?正确的位置是在哪里使用synchronized修饰符?目前正在实践中阅读Java Concurrency一书。这是一个增量这是一个非常广泛的问题—无论您在哪里需要以原子方式执行多个操作,并且与其他线程相互排斥。问题是,您在这里不需要它,因为
AtomicLong
提供了另一种(重量更轻)的方法。您在这里不需要任何同步,因为