据我了解,如果硬件在多处理器系统上支持缓存一致性,那么对共享变量的写入将对其他处理器上运行的线程可见。为了测试这个,我用 Java 和 pThreads 编写了一个简单的程序来测试这个
public class mainTest {
public static int i=1, j = 0;
public static void main(String[] args) {
/*
* Thread1: Sleeps for 30ms and then sets i to 1
*/
(new Thread(){
public void run(){
synchronized (this) {
try{
Thread.sleep(30);
System.out.println("Thread1: j=" + mainTest.j);
mainTest.i=0;
}catch(Exception e){
throw new RuntimeException("Thread1 Error");
}
}
}
}).start();
/*
* Thread2: Loops until i=1 and then exits.
*/
(new Thread(){
public void run(){
synchronized (this) {
while(mainTest.i==1){
//System.out.println("Thread2: i = " + i); Comment1
mainTest.j++;
}
System.out.println("\nThread2: i!=1, j=" + j);
}
}
}).start();
/*
* Sleep the main thread for 30 seconds, instead of using join.
*/
Thread.sleep(30000);
}
}
/* pThreads */
#include<stdio.h>
#include<pthread.h>
#include<assert.h>
#include<time.h>
int i = 1, j = 0;
void * threadFunc1(void * args) {
sleep(1);
printf("Thread1: j = %d\n",j);
i = 0;
}
void * threadFunc2(void * args) {
while(i == 1) {
//printf("Thread2: i = %d\n", i);
j++;
}
}
int main() {
pthread_t t1, t2;
int res;
printf("Main: creating threads\n");
res = pthread_create(&t1, NULL, threadFunc1, "Thread1"); assert(res==0);
res = pthread_create(&t2, NULL, threadFunc2, "Thread2"); assert(res==0);
res = pthread_join(t1,NULL); assert(res==0);
res = pthread_join(t2,NULL); assert(res==0);
printf("i = %d\n", i);
printf("Main: End\n");
return 0;
}
我注意到 pThread 程序总是结束。(我测试了 thread1 的不同睡眠时间)。然而,Java 程序只结束了几次;大多数时候都不会结束。如果我在 java 程序中取消注释 Comment1,那么它会一直结束。此外,如果我使用 volatile,那么它在所有情况下都以 java 结束。
所以我的困惑是,
如果缓存一致性是在硬件中完成的,那么除非编译器优化了代码,否则其他线程应该可以看到“i=0”。但是如果编译器优化了代码,那么我不明白为什么线程有时会结束而有时不会。还添加 System.out.println 似乎会改变行为。
任何人都可以看到导致这种行为的 Java 所做的编译器优化(C 编译器没有完成)吗?
即使硬件已经支持它,编译器是否还需要做一些额外的事情来获得缓存一致性?(如启用/禁用)
我应该默认对所有共享变量使用 Volatile 吗?
我错过了什么吗?欢迎任何其他意见。