Java 使用“同步”获取意外值

Java 使用“同步”获取意外值,java,multithreading,synchronized,Java,Multithreading,Synchronized,我有一个简单的代码片段,并尝试对其进行一些实验,但在接下来的代码中,我不清楚输出数据的顺序: public class Main { static int n = 100; public static synchronized int decreaseValue(){ return --n; } public static void main(String[] args) throws InterruptedException, IOExce

我有一个简单的代码片段,并尝试对其进行一些实验,但在接下来的代码中,我不清楚输出数据的顺序:

public class Main {

    static int n = 100;

    public static synchronized int decreaseValue(){
        return --n;
    }

    public static void main(String[] args) throws InterruptedException, IOException {


        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    try {
                        System.out.println("Thread1: "+ decreaseValue());
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        },"Thread1");
        t1.start(); 

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    try {
                        System.out.println("Thread2: "+ decreaseValue());
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        },"Thread2");
        t2.start();


        while(true){
            try {
                System.out.println("Main Thread: "+ decreaseValue());
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
无法理解,为什么我会在下一个顺序中获得这样的值:

线程1:89 螺纹2:90 主线:88

请注意N值,而不是调用线程的顺序:

Thread1: 99
Thread2: 98
Main Thread: 97
Main Thread: 95
Thread2: 94
Thread1: 96
Main Thread: 92

线程是并发运行的,没有任何东西会影响它们调用
decreaseValue()
函数的顺序。您希望它们是有序的,因为您正在执行相同数量的睡眠:),但一旦线程从睡眠开始/恢复,CPU就会将线程放入运行队列(创建执行顺序,这是同步将保证的顺序),因此,打印顺序取决于CPU如何将线程放入运行队列

如果您询问打印顺序,控制台上的打印也会同步(但不会与decreaseValue位于同一块中)。相同的逻辑适用于打印,就像减少值的逻辑一样


如果希望以与值递减相同的顺序查看打印,可以在
decreaseValue()
函数中移动打印。但这不会影响值递减的顺序

线程并行运行。你无法预测他们的执行顺序。您可以设置线程优先级,以确定其执行顺序的优先级

您一定在什么地方读到过,必须使用
synchronized
来确保正确的订购,或者类似的东西。“排序”一词属于一个不同于您心目中的概念:它意味着对同步块的执行总是有一些明确的排序。订单事先不知道,但每次都会有。如果没有同步,您甚至无法得到保证:一个线程可以感知一个顺序,另一个线程可以感知另一个顺序,或者根本无法感知其他线程的任何操作

关于您的编辑:
如果您担心打印输出出现错误,这是因为您的
println
语句在
synchronized
之外,因此可以独立于对
decreaseValue
的调用进行交错,但是我使用synchronized block并获得下一个顺序的输出值:89,90,88-所以看起来synchronized block不起作用。
synchronized
只确保一次只有一个线程可以执行该方法,它不保证任何顺序。但是在这种情况下,一个线程读取decrease方法中的n值时,另一种方法也可以访问它。@barn.gumbl你能提供更多的细节吗,为什么“看起来”?@barn.gumbl前三种方法是因为它们在队列中按顺序结束,然后CPU应用我的答案中给出的逻辑。看看我的最新答案你会期待什么?没有任何东西可以保证这些不同线程执行
DecreatedValue()
的顺序。没有任何东西可以保证哪个线程将调用Decreatease方法,但据我所知,synchronized块应该保证输出值的有效顺序。在这种情况下,同步DecreatedValue()毫无意义,因为基类型(除了long和double)上的算术运算保证是原子的。我没有注意到System.out.println不是一个同步的方法。@ignis递减整数不是原子的,赋值给整数是。没有什么可以保证哪个线程会调用Decreatease方法,但据我所知,同步块应保证输出值的有效顺序。在这个方法中,一个线程似乎也可以访问另一个线程。