为什么在 运行 下面的代码之后 count1 < count2? count1 和 count2 都应为 2000000

Why is count1 < count2 after running the following code? Both count1 and count2 should be 2000000

JDK11中运行编译并查看结果。 sum1 是同步代码所用的时间,sum2 是 AtomicInteger 代码所用的时间。 count1是对synchronized count++的调用次数进行统计的结果。 count2 是相同数量的组合调用,但使用 AtomicInteger。计数应为 2000000,预计 sum1 > sum2(以毫秒为单位)。但是,count1 明显更少,那么调用去哪里了?

github

package com.charlie;

import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;

public class AtomicTest extends Thread{

    private volatile Integer count1 = 0;
    private AtomicInteger count2 = new AtomicInteger(0);
    
    private void method1() {
        for (int i=0; i<1000; i++) {
            int a = i;
        }
        synchronized (count1){
            count1++;
        }
    }    
    private void method2() {
        for (int i=0; i<1000; i++) {
            int a = i;
        }
        count2.getAndIncrement();
    }
    
    public class Thread1 extends Thread{
        public Date start = null;
        public Date stop = null;
        @Override
        public
        void run() {
            start = new Date();
            for(int i=0;i<1000000;i++) {
                method1();
            }
            stop = new Date();
        } 
    }    
    public class Thread3 extends Thread{
        public Date start = null;
        public Date stop = null;
        @Override
        public
        void run() {
            start = new Date();
            for(int i=0;i<1000000;i++) {
                method2();
            }
            stop = new Date();
        } 
    }    
    static Thread1 t1 = null;
    static Thread1 t2 = null;
    static Thread3 t3 = null;
    static Thread3 t4 = null;
    static AtomicTest master = null;

    public static void main(String[] args) {
        AtomicTest master = new AtomicTest();
        t1 = master.new Thread1();
        t2 = master.new Thread1();
        t3 = master.new Thread3();
        t4 = master.new Thread3();
        master.start();
        t1.start();t2.start();t3.start();t4.start();
    }
    
    @Override
    public void run() {
        //stop and sum
        Boolean finished = false;
        while(!finished) {
            try {
                AtomicTest.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            if(t1.stop != null && t2.stop != null && t3.stop != null && t4.stop != null) {
                Long sum1 = (t1.stop.getTime() - t1.start.getTime()) 
                + (t2.stop.getTime() - t2.start.getTime());
                Long sum2 = (t3.stop.getTime() - t3.start.getTime())
                + (t4.stop.getTime() - t4.start.getTime());
                finished = true;
                System.out.println("sum1 =" + sum1 + "ms sum2 =" + sum2 + "ms");
                System.out.println("count1 =" + count1 + " count2 =" + count2);
            }
        }
    }
}

不要对可能重复使用的对象进行同步。如果您想了解更多,请阅读此 article

在您的情况下,避免这种情况的一个简单解决方案是用一个空对象锁定。

private Object key = new Object();
private volatile Integer count1 = 0;

private void method1() {
    for (int i=0; i<1000; i++) {
        int a = i;
    }
    synchronized (key){
        count1++;
    }
}    

a Java 整数是不可变的,所以当你对它使用 ++ 时,它会创建一个新对象。如果您在这些对象上进行同步,它们将具有不同的身份,因此这将不起作用。要修复它,您可以将 Integer 更改为普通 int(如果您不需要),但创建一个单独的 Object 进行同步。例如私人对象同步=新对象();从 Integer 更改为 plain int 也应该加快速度。

此作品的更改现在 github 中。谢谢 Magyar Tamas 和 Jason Splashett。