代码之家  ›  专栏  ›  技术社区  ›  thelordvampire

System.out.println使用java易失性

  •  1
  • thelordvampire  · 技术社区  · 6 年前

    我有一个这样的例子:

    public class MainApp {
    
        private volatile static int MY_INT = 0;
    
        public static void main(String[] args) {
            new Thread1().start();
            new Thread2().start();
        }
    
        static class Thread1 extends Thread {
            @Override
            public void run() {
                while(true) {
                    MY_INT++;
                    System.out.println("1 : " + MY_INT);
                }
            }
        }
    
        static class Thread2 extends Thread{
            @Override
            public void run() {
                while(true) {
                    MY_INT++;
                    System.out.println("2 : " + MY_INT);
                }
            }
        }
    }
    

    1 : 1
    2 : 2
    1 : 3
    2 : 4
    1 : 5
    1 : 7
    1 : 8
    1 : 9
    1 : 10
    2 : 6
    1 : 11
    1 : 13
    

    我不明白为什么打印1:10,下一行是2:6。谁能解释一下结果吗?提前谢谢

    1 回复  |  直到 6 年前
        1
  •  3
  •   meriton    6 年前

    中的PrintStream System.out MY_INT 独立同步,因此可能发生以下情况:

    Thread 1               Thread 2
    read MY_INT = 4
    write MY_INT = 5
    read MY_INT = 5
                           read MY_INT = 5
                           write MY_INT = 6
                           read MY_INT = 6
    println 5
    read MY_INT = 6
    write MY_INT = 7
    read MY_INT = 7
    println 7
    
    ...
                           println 6
    

    也就是说,因为volatile字段和PrintStream返回系统输出是独立同步的,打印可以按非升序进行。

    也可能发生以下情况:

    Thread 1            Thread 2
    read MY_INT = 1
                        read MY_INT = 1
    write MY_INT = 2
                        write MY_INT = 2
    read MY_INT = 2
    println 2
                        read MY_INT = 2
                        println 2
    

    因为++我的INT实际上被编译成一个读、一个计算和一个写。由于volatile读取和写入是独立的同步操作,所以其他线程可能会在这两个操作之间进行操作,从而使计数器混乱。

    如果您想用单独的线程打印升序数字,最简单的方法是:

    void run() {
       while (true) {
           synchronized (lock) {
               MY_INT++;
               System.out.println("1 : " + MY_INT);
           }
       }
    }
    

    哪里 lock

        2
  •  4
  •   Thomas Weller    6 年前

    这里有几个问题:

    • timer resolution (Microsoft) ). 这就是你看不见的原因 1:x 2:x 一个接一个,但是几个 一个接一个。
    • 使用 volatile AtomicInteger 或者 synchronized ++ 以及 println() 如果你想看到唯一的号码
    • println 语句在一行上混合输出