fengdavid 2015-02-10 15:38 采纳率: 100%
浏览 1787
已采纳

请教一个JAVA线程的奇怪问题

在学习JAVA线程时候的遇到一个很奇怪的现象。让我们先来看代码

public class TestThread {

    public static void main(String[] args) {
        IRun ir = new IRun();
        Thread it = new Thread(ir);
        it.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException ex) {
            Logger.getLogger(TestThread.class.getName()).log(Level.SEVERE, null, ex);
        }
        ir.setStop();
    }

    static class IRun implements Runnable {
        boolean exec = true;
        public void setStop() {
            exec = false;
            System.out.println("exec = " + exec);
        }
        @Override
        public void run() {
            int c = 0;
            while (exec) {
                c++;
            }
            System.out.println("退出了循环");
        }
    }
}
     在上述程序首先创建一个Runnable对象ir,然后创建一个线程对象it,通过it.start()启动线程,执行IRun类中的run()函数,执行一个while循环。while循环的条件由布尔变量exec控制。主程序中等待1秒钟后,修改exec的值为false,按照正常的逻辑应该是在exec值为false后,循环结束执行下面输出语句,然后线程结束,程序退出。
    但是,上述代码在运行后,将不会执行run()函数中的System.out.println("退出了循环");语句,程序一直保持运行。对这种现象,在帖子”关于JAVA线程,请大神帮忙“中still_rain给出的这种现象的原因是线程访问的同步问题导致,当主线程(main函数所在的线程)中修改变量exec的值后,在创建执行的线程中访问的exec的值没有发生变化(或者说可能是主线程、执行线程这两个线程exec变量是不同的对象,好像是说java会将一些资源在每个线程中复制一份?有待确认)。在exec变量前加上volatile修饰符后,程序运行正常,问题似乎解决。
   现在以上述代码为基础,进行一次小小的修改。在run()函数中的while循环中加上一个类对象操作,比如新建一个字符串,则run()函数为
 public void run() {
            int c = 0;
            while (exec) {
                String s = new String("");
                c++;
            }
            System.out.println("退出了循环");
        }
    } 
   运行程序,程序按预期的顺序执行,顺利结束。这时候不管exec变量是否有volatile修饰符,且只在while循环中存在类对象操作(新建对象、对象函数调用(调用的函数不能返回基本数据类型),以及像System.out.println这样的操作),程序的运行都很正常,对这种现象就不是上述的同步问题能够解释的了。在帖子”关于JAVA线程,请大神帮忙“中still_rain(感谢热情的回复)从编译器优化的角度进行了解释。当while循环中只有简单的基本数据类型参与运算的时候由于执行速度太快,将while循环编译成了while(true)语句。导致循环不会退出,从而循环后的输出不会执行,且程序不会退出。
        为了探究这个奇怪现象的原因,让我们看看编译后的字节码,下图是用jclasslib查看的字节码,是修改前的run()函数字节码。

从图中可以看到,循环体从aload_0开始,到goto 2结束。控制跳转的语句是ifeq 15,意思是如果值为0则跳转到15,否则执行下面的语句。
当将上面的while(exec)语句修改为while(true)后,字节码为:

由两图可以看出,编译器并没有将while(exec)语句优化为while(true)。再看修改后的代码编译的字节码:

循环体从aload_0起,到goto 2结束。从ifeq 25下面的一行到astotre_2行,为String s = new String("");语句的字节码,将这段去掉则字节码与修改前的一样。所以基本可以排除编译器优化导致的程序不正常运行。
请看到这篇文章的大神们能够给予指导,探究出现这种情况的原因,谢谢

  • 写回答

6条回答 默认 最新

  • fengdavid 2015-02-12 06:58
    关注

    原因找到了,32位JDK下没有问题,只要是64位JDK都会出现这各现象
    (多谢 搞几年和程序员小董的回复)

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(5条)

报告相同问题?

悬赏问题

  • ¥15 Attributeerror:super object has no attribute '__sklearn_tags__'_'
  • ¥15 逆置单链表输出不完整
  • ¥15 宇视vms-B200-A16@R启动不了,如下图所示,在软件工具搜不到,如何解决?(操作系统-linux)
  • ¥500 寻找一名电子工程师完成pcb主板设计(拒绝AI生成式答案)
  • ¥15 关于#mysql#的问题:UNION ALL(相关搜索:sql语句)
  • ¥15 matlab二位可视化能否针对不同数值范围分开分级?
  • ¥15 已经创建了模拟器但是不能用来运行app 怎么办😭自己搞两天了
  • ¥15 关于#极限编程#的问题,请各位专家解答!
  • ¥20 win11账户锁定时间设为0无法登录
  • ¥45 C#学生成绩管理系统