Java 为什么这个程序在无限循环中运行?互斥

Java 为什么这个程序在无限循环中运行?互斥,java,multithreading,scheduler,mutual-exclusion,Java,Multithreading,Scheduler,Mutual Exclusion,下面是我开始学习互斥的java程序 class MutexVar{ public static int Turn = 1; } class CriticalSection{ private static int modelNumber =0; public static void setModelNumber(int number){ modelNumber = number; } public static int getModelN

下面是我开始学习互斥的java程序

class MutexVar{
    public static int Turn = 1;
}

class CriticalSection{
    private static int modelNumber =0;
    public static void setModelNumber(int number){
        modelNumber = number;
    }

    public static int getModelNumber(){
        return modelNumber;
    }
}

public class MutualExclusion{
    public static void main(String[] args){
        Thread threadObjRef = new Thread(new Runnable(){
            public void run(){
                int autoVar = 1;
                int newModelNumber = -1;
                while(true){
                    /*                               Non - critical section - start   */
                    System.out.println("\n" + "In  run() thread");
                    /*                               Non - critical section - end     */
                    while(MutexVar.Turn == 2){
                        //System.out.println("loop-run"); //infinite loop problem is here
                    }
                    /*                               Critical Section -start          */
                    CriticalSection.setModelNumber(autoVar);
                    newModelNumber = CriticalSection.getModelNumber();
                    MutexVar.Turn = 2;
                    /*                               Critical Section -end            */

                    /*                               Non - critical section - start   */
                    System.out.println("run() thread: " + newModelNumber + "\n");
                    autoVar = autoVar + 2;
                    /*                               Non - critical section - end     */
                }
            }
        });
        threadObjRef.start();
        int autoVar = 0;
        int newModelNumber = -1;
        while(true){

            /*                                       Non - critical section - start    */
            System.out.println("\n" + "In  main thread");
            /*                                       Non - critical section - end      */
            while(MutexVar.Turn == 1){
                //System.out.println("loop-main"); //infinite loop problem is here
            }
            /*                                       Critical Section -start           */
            CriticalSection.setModelNumber(autoVar);
            newModelNumber = CriticalSection.getModelNumber();
            MutexVar.Turn = 1;
            /*                                       Critical Section -end             */

            /*                                       Non - critical section - start    */
            System.out.println("main- thread: " + newModelNumber + "\n");
            autoVar = autoVar + 2;
            /*                                       Non - critical section - end      */
        }
    }
}
我的问题:

1) 设置
MutexVar.Turn
的两个线程之间是否存在数据竞争

2) 如果没有,那么这个程序对我来说很好,但是我的程序无限循环,输出如下。为什么无限循环
循环运行
循环主循环

In  main thread

In  run() thread
run() thread: 1


In  run() thread
我的观察:


这看起来像是线程调度问题。我了解到java线程对windows操作系统是可见的,因为它们是在内部使用
kernel32.dll的
CreateThread()
api创建的,并由操作系统在windows中作为1-1线程模型进行调度。java程序在Windows7多核操作系统上使用JavaJDK1.6运行

一开始你的代码读错了。看起来您有一个过时的值

MutexVar.Turn
s值不会刷新,以便其他线程看到最新的更改。如果在读取和写入线程之间共享的公共变量时,将
Turn
更改为在某个公共锁上声明为volatile或synchronized,那么您可能会发现
MutexVar。将
s值更改。

始终同步对共享可变数据的访问。JIT/CPU正在做什么并不十分明显,但通过使用非易失性
Turn
,您几乎肯定会遇到线程缓存问题。将
MutrexVar
声明为

static class MutexVar {
    public static volatile int Turn = 1;
}
关键字Volatile表示,读取此值的每个线程都将具有最新的值,并禁止编译器重新排序


编译器重新排序的更多细节。JIT编译器能够读取您的代码并提升对
Turn
的读取。例如,它可以转换

while(MutexVar.Turn == 1){
    //System.out.println("loop-main"); //infinite loop problem is here
}
进入


这绝不会违反Java的编译器契约,并且可以显著提高性能。声明字段volatile可防止此类型或重新排序。

为什么操作系统不在两个线程之间交错?我想这是我想理解的一点。用某种特定语言编写的程序不可能成为问题。在后台,Java可以缓存值,只要它不违反线程内语义(程序顺序)。操作系统所做的正是JIT将代码编译成的。是的,它在设置为
volatile
后工作。但是,如果我比较字节码,两种情况下都是一样的。在run()线程中,它是:
Line#31 getstatic 41;/*MutexVar.Turn*/
在main()线程中是:
行MutexVar.Turn*/
尽管我使用了
volatile
关键字yes!字节码最初通过编译源代码生成一次。您希望看到的是在程序集运行时生成的程序集。初始编译通常是非操作化的。JIT在运行时需要更多关于如何更改它的信息。看看通过
-XX:+UnlockDiagnosticVMOptions-XX:+PrintAssembly
启用程序集输出它,我想,
.java
被编译成
.class
,然后JVM逐个指令解释
.class
指令。不是吗?JIT是在哪个阶段出现的?是的,它与
volatile
一起工作,但是当我们声明volatile时,到底发生了什么。因为
main(){}
的字节码看起来与我相似,带有/不带volatile关键字。有没有出现这个问题,,如果这个程序是用C/C++?@overexchange编写的,那么Java编程语言指定,如果没有用于在线程之间共享可变状态的适当同步工具,JVM可以自由地进行优化,从缓存值到刷新值到RAM,再到重新排序语句,因为它假定数据不可用如果使用volatile/synchronized标记,则在线程之间共享。字节码可能不会有太大的变化,这里或那里可能有一两个标志,您将看到的真正区别是JVM编译(JIT)字节码时的实际程序集。@overexchange您可以使用此问题中提到的编译器标志来查看JVM编译字节码时的程序集输出:。我对c/c++不太熟悉,但我的理解是,这些语言规范没有指定线程之间共享状态的行为方式,因此这可能取决于编译器实现和您使用的线程库(您可能仍然需要进行某种刷新/锁定组合).@overexchange还可以使用-Xcomp标志,确保JVM在第一次使用时编译您的字节码,以便您可以看到输出。@overexchange这是一项功能。本质上,如果您没有将一个值标记为可能被多个线程使用(通过使用公共同步锁读取和写入该值,或将其声明为volatile),JVM可以进行优化,从对语句重新排序到在CPU缓存中保留一个值,并在经常使用的情况下对其进行修改(与每次将最新值写回主内存不同。如果最新值在某种缓存中停留了一段时间,而其他线程看不到,那么这些线程将认为它是过时的值)。
if(MutexVar.Turn == 1) {
    while(true) {
        //System.out.println("loop-main"); //infinite loop problem is here
    }
 }