为什么线程在不同的run方法体上表现不同?

时间:2015-02-16 15:00:11

标签: java multithreading synchronization volatile

此代码来自Effective Java(第66项):(没有同步或易失性,这永远不会结束)

public class ThreadPractice {
static boolean canrunstatic;

public static void main(String[] args) throws InterruptedException {

    Thread backgroundThread = new Thread(new Runnable() {
        public void run() {
            int i = 0;
            while (!canrunstatic){i++;}
            System.out.println("finished");
        }
    });
    backgroundThread.start();
    TimeUnit.SECONDS.sleep(1);
    canrunstatic = true;
}
正如布洛赫在该章中提到的那样,它永远不会写完"完成"到控制台。我一直在玩这个类,并将该行添加到runnable run方法中:

System.out.println("im still running");

有了这个,while循环不仅仅增加i,而是在每个循环中打印出这个字符串。但令我疯狂的是,这样一来,当主线程从睡眠状态恢复时,线程会在1秒后停止。

修改:(停止没有易失/同步)

public class ThreadPractice {
static boolean canrunstatic;


public static void main(String[] args) throws InterruptedException {

    Thread backgroundThread = new Thread(new Runnable() {
        public void run() {
            int i = 0;
            while (!canrunstatic){i++;System.out.println("im still running");}
            System.out.println("finished");
        }
    });
    backgroundThread.start();
    TimeUnit.SECONDS.sleep(1);
    canrunstatic = true;


}

那背后的逻辑是什么?

2 个答案:

答案 0 :(得分:4)

确切地说,线程将永远停止,无法保证,但禁止它确实停止。这背后的逻辑由Java Memory Model提供,这是一个相当复杂的主题,但是为了理解Java中的多线程需要。

这个概念是,如果这两个动作彼此同步,则只需要另一个线程看到对一个线程的非易失性字段的写入。如果执行的线程所显示的行为不会改变,则允许编译器对某些操作重新排序。但另一个线程可能会看到这个。因此,您需要正确的同步,以告诉编译器某些部分不允许重新排序。

在此处阅读有关此内容的完整文章:JSR-133

答案 1 :(得分:2)

将数据写入控制台通常是通过线程安全操作实现的。

在这种情况下,您向控制台写入数据的行为也可以触发canrunstatic所见backgroundThread变量的更新。

请注意,这不是Java内存模型所承诺的,也不是java System.out的实现