package net.jcip.examples;public class NoVisibility {
static boolean ready;
static int number; private static class ReaderThread extends Thread {
public void run() {
while (!ready) {
Thread.yield();
}
System.out.println(number);
}
} public static void main(String[] args) throws InterruptedException {
new ReaderThread().start();
// 这个是我特意加的,让上面的线程先跑起来
Thread.sleep(1000);
number = 41;
ready = true;
}
}
上面这段代码是书上的,书上说这段代码中的while可能会一直循环,因为对于读线程来说,ready的值可能永远不可见。甚至更奇怪的
现象是,NoVisibility可能会打印0,因为早在对number赋值之前,主线程就已经写入ready并使之对读取线程可见。可是我运行下来,始终打印出41,加不加sleep那段话都一样。什么原因呢?
static boolean ready;
static int number; private static class ReaderThread extends Thread {
public void run() {
while (!ready) {
Thread.yield();
}
System.out.println(number);
}
} public static void main(String[] args) throws InterruptedException {
new ReaderThread().start();
// 这个是我特意加的,让上面的线程先跑起来
Thread.sleep(1000);
number = 41;
ready = true;
}
}
上面这段代码是书上的,书上说这段代码中的while可能会一直循环,因为对于读线程来说,ready的值可能永远不可见。甚至更奇怪的
现象是,NoVisibility可能会打印0,因为早在对number赋值之前,主线程就已经写入ready并使之对读取线程可见。可是我运行下来,始终打印出41,加不加sleep那段话都一样。什么原因呢?
我把代码修改了一下,如下:package queston2;
public class NoVisibility {
static boolean ready;
static int number; private static class ReaderThread extends Thread {
public void run() {
System.out.println(ready);
while (!ready) {
System.out.println("Start to yield!");
Thread.yield();
System.out.println("Restore from yield!");
}
System.out.println(number);
}
} public static void main(String[] args) throws InterruptedException {
new ReaderThread().start();
// 这个是我特意加的,让上面的线程先跑起来
Thread.sleep(1000);
System.out.println("Main thread Sleep end");
number = 41;
ready = true;
}
}其结果是:
false
Start to yield!
Restore from yield!
...
...
...
Start to yield!
Main thread Sleep end
Restore from yield!
41以上的运行结果说明,程序先从main函数开始运行,main是一个线程,然后main函数新建了ReaderThread方法所在线程,然后main函数执行了sleep(1000).
ReaderThread方法中ready是明显可见的,因为运行结果第一行中有false输出。不过这个false并不是main函数赋值的,而是JVM对NoVisibility类的静态成员变量赋的初值。
因为ready的值此时为false,说以while(!ready){Thread.yield()}肯定是可以运行的。运行后ReaderThread进入yield状态,根据JAVA的线程规则,处于yield状态的线程会把运行权让给同样优先级的main线程,可是此时main线程正在执行sleep(1000)中,还不能运行,因此又将执行权交回ReaderThread进程,由于main在sleep(1000)结束以前都不会改变ready的值,因此while(!ready)依然为真,继续yield(),如此一直循环。所以结果中有重复的 Start to yield!/Restore from yield!
直到main函数从sleep(1000)中恢复,这时ReaderThread线程依然会不停地yield,因此同样优先级的main线程便执行number = 41;ready = true;等代码,改编了成员变量的值,然后main线程就结束了。此时ReaderThread依然yield,但是由于main线程已经结束,就剩下ReaderThread自己,再没有别的线程可让了。因此ReaderThread线程执行while(!ready),这是ready已经让main线程修改为ture,所以while(!ready)为假,这时就跳出了while循环执行打印number的值,这是打印41.
执行结果就是这样。
结论:
ready是在ReaderThread是可见的,因此说ready不可见不对。
while()循环确实是要执行很多次,但是由于while循环的条件受main线程的影响,因此是有结束的。
也就是线程A改了某个变量a,线程B却不知道变量a已经改了的情况。
同步的话,线程B肯定能知道a变量已经改变了,以为此时线程A已经修改了变量a,并且已经释放了锁.
我所说的是不同步,而且两个线程同时对变量a访问或修改的情况.