fengdavid
fengdavid
2015-02-10 15:38

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

  • 线程中止
  • 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条回答

为你推荐