关于java重排序的问题

李朝 2014-02-24 05:10:50
今天看《java并发编程实战》看到重排序的地方P28,这个例子始终都没有成功,代码如下:
public class NoVisibility {  
private static boolean ready;
private 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) {
new ReaderThread().start();
number = 42;
ready = true;
}
}

按作者意思,这里打印的结果很可能是0,或者根本无法终止,因为在代码中没有使用足够的同步机制,因此无法保证主线程写入的ready值和number值对于读线程来说是可见的;P28
但是本人测试n次,均打印出来的是42,然后本人用1000个线程运行得到的结果也都是42,求大神解答

public class NoVisibility {
private static class ReaderThread extends Thread {
private WriteThread writeThread;
ReaderThread(WriteThread wt){
writeThread = wt;
}
@Override
public void run() {
while (!writeThread.ready) {
Thread.yield();
}
System.out.println(writeThread.number);
}
}
private static class WriteThread extends Thread{
public boolean ready;
public int number;

@Override
public void run() {
number = 42;
ready = true;
}
}
public static void main(String[] args) {
for (int i = 0; i <1000; i++){
WriteThread wt = new WriteThread();
new ReaderThread(wt).start();
wt.start();
}
}
}
...全文
608 15 打赏 收藏 转发到动态 举报
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
gaofuqi 2014-12-12
  • 打赏
  • 举报
回复
http://bbs.csdn.net/topics/390757750 已经在上面的帖子回复过了,复制一份: 首先类加载会初始化ready和number的值分别为false和0,可能会出现的情况应该有4种,楼主执行的结果是一种, 剩下的3种情况应该是这样的: 1.无限循环,number的值为0:在主线程即main方法中对ready的设置(即ready = true)还没来得及写回主存(静态变量保存在方法区),ReaderThread 线程就已经读取了ready的值(并保留了副本),然后加载到Java栈中,此时ready 一直为false所以出现死循环。number的值也可以类似推理,在主线程即main方法中对number的设置(即number= 42)还没来得及写回主存(静态变量保存在方法区),ReaderThread 线程就已经读取了number的值(并保留了副本),然后加载到Java栈中,此时number一直为0(只是没有打印出来而已); 2.无限循环,number的值为42:在主线程即main方法中对ready的设置(即ready = true)还没来得及写回主存(静态变量保存在方法区),ReaderThread 线程就已经读取了ready的值(并保留了副本),然后加载到Java栈中,此时ready 一直为false所以出现死循环。在主线程即main方法中对number的设置(即number= 42)后(即number的值已经写回了主存),ReaderThread 线程才开始执行此时读取的number为42(只是没有打印出来而已); 3.输出0:在主线程即main方法中对ready的设置(即ready = true)后(即ready的值已经写回了主存),还没来得及写回主存(静态变量保存在方法区),ReaderThread 线程就已经读取了number的值(并保留了副本),然后加载到Java栈中,此时number为0; 至于为什么会出现ready = true写回主存后,number = 42还没写回主存。这应该是由于Java虚拟机的一种优化技术叫指令重排序,number = 42不一定会在ready = true前面执行,得看Java虚拟机是怎么优化的。
  • 打赏
  • 举报
回复
main也是一个线程,主线程一般先运行的,等于number=42,ready=true,你想让主线程慢点执行,你得用jion吧。
as1dasd11 2014-12-12
  • 打赏
  • 举报
回复
这个和CPU有关,而且发生的几率非常低,还是不要试了。。。
ningbohezhijun 2014-12-11
  • 打赏
  • 举报
回复
你看下9楼的说法吧,书上想表述的就是这个原理。如果你看了重排序的资料就应该明白。
wyc_ 2014-12-09
  • 打赏
  • 举报
回复
polygram81 2014-12-09
  • 打赏
  • 举报
回复
这个问题我也试过了,没有得到想要的结果,后来想想未必作者说的是错的,作者说的出现的状况本来就是极端的情况,至于是否能出现这个状况,要看重排序的结果,以及线程调度机制的时间片段分配,这几个短短的操作其实才3毫秒左右。 private static class sonThread extends Thread{ public void run(){ while(!ready){ System.out.println("交出控制权时间:"+System.currentTimeMillis()); Thread.yield(); } System.out.println(number); System.out.println("结束时间:"+System.currentTimeMillis()); } } public static void main(String[] args) { new sonThread().start(); number=42; System.out.println("number赋值时间:"+System.currentTimeMillis()); ready=true; System.out.println("reder赋值时间:"+System.currentTimeMillis()); } 这是我想看的程序运行轨迹 number赋值时间:1418089495926 reder赋值时间:1418089495929 交出控制权时间:1418089495926 42 结束时间:1418089495931
a12939026 2014-12-09
  • 打赏
  • 举报
回复
你用的是不是client模式, client模式不会冲排序的 你试试用-server启动,可能就有效果了
冥王之锤 2014-12-09
  • 打赏
  • 举报
回复
重排列基于CPU,编译器的各种不同选项。一般我们也不具备各种cpu,编译器,编译器选项的测试条件,这个重排列很难测出来。
wyc_ 2014-12-09
  • 打赏
  • 举报
回复
书上说的没错,由于线程调度和重排序的原因,main函数中各个语句以及主线程与其他线程的执行顺序是不确定的。所以作者说的情况的确是有可能发生的。但是主线程在被调度器交换下来之前极有可能number = 42; ready = true;两个语句全部执行完了,然后结果就是每次打印42. number = 42;ready = true;这两处修改在线程里面有可能不可见,也就是说主线程修改了自己的调用栈里面的这两个值,而没有将其刷新到主存。其他线程也没有进行副本的重新拷贝,这样就有可能导致死循环。这里表现出来的好像两个变量是volatile类型一样,这一点我也不理解。
kainever 2014-12-09
  • 打赏
  • 举报
回复
我觉得 书上这么说是在表达一种意思 , 如果没有设置同步的话 可能 线程A运行到while 语句时 - > 线程B readey - true 而现在 ready - true , 而你仍然会运行 yield ()... 不设置同步的话 就不知道 到底执行的那个方法... 但是我搞不懂的是, 为什么会可能出现 ready 一直为false 的情况了, 都是指向同一块内存...
卡卡吉利 2014-02-24
  • 打赏
  • 举报
回复
引用 4 楼 A289048093 的回复:
[quote=引用 1 楼 lwb314 的回复:] 你根本没有改变number的地方,当然不变了
main方法里面有number=42赋值了,原文作者的意思是“编译器、处理器以及运行时都可能对操作的执行顺序进行一些意向不到的调整”,就是说可能在运行的时候ReaderThread读到ready=true的时候,可能还没读到number=42这里(因为可能存在重排序问题,ReadThread读到的结果是先ready=true,这里就开始运行打印操作了,读到的值是初始值(0),然后读到number=42),这样导致打印结果可能是0,但是我没能得到这个结果。[/quote]他说的编译器优化,可能是指两个赋值同步进行导致,应该是非常极端的情况
李朝 2014-02-24
  • 打赏
  • 举报
回复
引用 1 楼 lwb314 的回复:
你根本没有改变number的地方,当然不变了
main方法里面有number=42赋值了,原文作者的意思是“编译器、处理器以及运行时都可能对操作的执行顺序进行一些意向不到的调整”,就是说可能在运行的时候ReaderThread读到ready=true的时候,可能还没读到number=42这里(因为可能存在重排序问题,ReadThread读到的结果是先ready=true,这里就开始运行打印操作了,读到的值是初始值(0),然后读到number=42),这样导致打印结果可能是0,但是我没能得到这个结果。
李朝 2014-02-24
  • 打赏
  • 举报
回复
引用 楼主 A289048093 的回复:
今天看《java并发编程实战》看到重排序的地方P28,这个例子始终都没有成功,代码如下:
public class NoVisibility {  
    private static boolean ready;  
    private 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) {  
        new ReaderThread().start();  
        number = 42;  
        ready = true;  
    }  
}  
按作者意思,这里打印的结果很可能是0,或者根本无法终止,因为在代码中没有使用足够的同步机制,因此无法保证主线程写入的ready值和number值对于读线程来说是可见的;P28 但是本人测试n次,均打印出来的是42,然后本人用1000个线程运行得到的结果也都是42,求大神解答

public class NoVisibility {
    private static class ReaderThread extends Thread {
        private WriteThread writeThread;
        ReaderThread(WriteThread wt){
            writeThread = wt;
        }
        @Override
        public void run() {
            while (!writeThread.ready) {
                Thread.yield();
            }
           System.out.println(writeThread.number);
        }
    }
    private static class WriteThread extends Thread{
        public  boolean ready;
        public  int number;

        @Override
        public void run() {
            number = 42;
            ready = true;
        }
    }
    public static void main(String[] args) {
        for (int i = 0; i <1000; i++){
            WriteThread wt = new WriteThread();
            new ReaderThread(wt).start();
            wt.start();
        }
    }
} 
main方法里面有number=42啊,就这里赋值了,原文作者的意思是“编译器、处理器以及运行时都可能对操作的执行顺序进行一些意向不到的调整”,就是说可能在运行的时候ReaderThread读到ready=true的时候,可能还没读到number=42这里(因为可能存在重排序问题,ReadThread读到的结果是先ready=true,这里就开始运行打印操作了,读到的值是初始值(0),然后读到number=42),这样导致打印结果可能是0,但是我没能得到这个结果。
卡卡吉利 2014-02-24
  • 打赏
  • 举报
回复
莫非这是一个错误的错误例子
  • 打赏
  • 举报
回复
你根本没有改变number的地方,当然不变了

62,614

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧