Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/322.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 使用volatile int进行多线程处理的有趣案例_Java_Android_Multithreading_Thread Synchronization_Java Threads - Fatal编程技术网

Java 使用volatile int进行多线程处理的有趣案例

Java 使用volatile int进行多线程处理的有趣案例,java,android,multithreading,thread-synchronization,java-threads,Java,Android,Multithreading,Thread Synchronization,Java Threads,嗯。我写了代码,得到了意想不到的结果,我不知道如何解释这个结果。有人能帮我吗 public class JMM { static volatile Boolean ready = false; static volatile int data = 0; public static void main() { Log.d("JMM", "start"); new Thread(new Runnable() { @Overri

嗯。我写了代码,得到了意想不到的结果,我不知道如何解释这个结果。有人能帮我吗

public class JMM {
static volatile Boolean ready = false;
static volatile int data = 0;

public static void main() {
    Log.d("JMM", "start");
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            data = 1;
            ready = true;
        }
    }).start();

    for(int i = 0; i < 100; i++) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (!ready)
                    Log.d("JMM", "second thread data " + data);
            }
        }).start();
    }
}
}
结果:

D/JMM:第二个线程数据0

D/JMM:第二个线程数据0

D/JMM:第二个线程数据0

D/JMM:第二个线程数据0

D/JMM:第二个线程数据0

D/JMM:第二个线程数据0

D/JMM:第二个线程数据1

D/JMM:第二个线程数据1

D/JMM:第二个线程数据1

D/JMM:第二个线程数据0

D/JMM:第二个线程数据0

D/JMM:第二个线程数据1

D/JMM:第二个线程数据1

D/JMM:第二个线程数据1

D/JMM:第二个线程数据1


我期待什么?默认情况下,int是原子类型(不过我以前写过volatile),它不会缓存它的值。但我看到不同的线程在同一时刻从一个字段读取不同的值。谁能给我解释一下呢?

想想这一行发生了什么:

Log.d("JMM", "second thread data " + data);
  • 读取
    数据
  • 将其转换为字符串并在处与“第二个线程数据”连接
  • 将两个参数传递给Log.d
  • 它最终打印出信息
  • 在第一步之后会发生很多事情,很可能一个线程会一个接一个地开始第1步,但在第4步之前就开始了。例如:

    Thread 1               | Thread 2
    -----------------------+-----------------------
    1. read "data"         |
    2. concat string:      |
      "...data 0"          |
         <<< third thread updates data = 1 >>>
                           | 1. read "data"
                           | 2. concat string:
                           |   "... data 1"
                           | 3. invoke Log.d(...)
                           | 4. print message
                           |   with "... data 1"
    3. invoke Log.d(...)   | 
    4. print message       |
      with "data 0"        |
    
    螺纹1 |螺纹2
    -----------------------+-----------------------
    1.读取“数据”|
    2.concat字符串:|
    “…数据0”|
    >
    | 1. 读取“数据”
    | 2. concat字符串:
    |“……数据1”
    | 3. 调用Log.d(…)
    | 4. 打印消息
    |带有“…数据1”
    3.调用Log.d(…)|
    4.打印消息|
    使用“数据0”|
    
    请将Log.d(“JMM”,“第二线程数据”+数据)替换为Log.d(“JMM”,String.format(“%s:%s at%s”,thread.currentThread(),data,System.currentTimeMillis()))并给我们新的输出,在您的情况下,似乎“volatile”不提供“before”订购。您使用的是什么环境,jre供应商版本,编译器?@NicolasFilotto这里是链接,这里有一些数据为1的日志。请您澄清一下“另一个线程将在稍后启动1,但在之前进入步骤4”是什么意思?在这种情况下,首先写入“数据”,然后写入“准备就绪”(由于易失性,不能应用重新排序)。根据JMM,这种情况下的记忆效应应该发生在关系之前,因此当“ready”变为“true”时,数据必须为“1”。这就是为什么当调用“Log.d”时,“data”的值必须是“1”,并且它与您显示的步骤没有任何关系。@hahn我更新了我的答案。请注意,“发生在”关系仅在读取变量时适用(例如,在本地寄存器中,以便可以将其传递给字符串连接方法)。这并不意味着,如果volatile变量随后发生更改,则从该读取中派生的任何值都会自动更新。我应该更准确地读取代码。我读的不是“while(!ready)”,而是“while(ready)”。无论如何,谢谢你。。
    Thread 1               | Thread 2
    -----------------------+-----------------------
    1. read "data"         |
    2. concat string:      |
      "...data 0"          |
         <<< third thread updates data = 1 >>>
                           | 1. read "data"
                           | 2. concat string:
                           |   "... data 1"
                           | 3. invoke Log.d(...)
                           | 4. print message
                           |   with "... data 1"
    3. invoke Log.d(...)   | 
    4. print message       |
      with "data 0"        |