一个关于java内存可见性的问题

zjy7554 2018-04-11 01:44:07
下面的代码是一直循环,但是如果把29行的synchronized(lock)注释去掉,程序就立刻退出来,能解释为什么吗?
我查了一些资料,“当读线程获取锁的时候,强制从主内存读取。写进程释放锁,会强制刷新到主内存”但是我的InnerStop 没有获取锁?哪位大侠能解释为什么吗
package current;
//主类
class TestJMM {
public static void main(String[] args) {
for(int i=0;i<1;i++){
InnerRun jmm=new InnerRun();
jmm.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
new InnerStop(jmm).start();
}


}
}
//工作线程
class InnerRun extends Thread{
boolean stop ;
StringBuffer sb=new StringBuffer();
Object lock=new Object();
int i=0;
@Override
public void run() {
while(!stop){
//synchronized(lock){
i++;
//}
}
}

public void stopMe() {
stop = true;
}
}
//停止线程
class InnerStop extends Thread{
InnerRun jmm=null;
public InnerStop(InnerRun jmm){
this.jmm=jmm;
}
@Override
public void run() {
jmm.stopMe();
}
}

...全文
734 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
a1282379904 2018-04-17
  • 打赏
  • 举报
回复
还有 https://en.wikipedia.org/wiki/Loop-invariant_code_motion
a1282379904 2018-04-17
  • 打赏
  • 举报
回复
引用 7 楼 zjy7554 的回复:
[quote=引用 6 楼 a1282379904 的回复:] Changes to fields made by one thread are guaranteed to be visible to other threads only under the following conditions: 1.A writing thread releases a synchronization lock and a reading thread subsequently acquires that same synchronization lock.In essence, releasing a lock forces a flush of all writes from working memory employed by the thread, and acquiring a lock forces a (re)load of the values of accessible fields 2.As a thread terminates, all written variables are flushed to main memory. For example, if one thread synchronizes on the termination of another thread using Thread.join, then it is guaranteed to see the effects made by that thread The memory model guarantees that, given the eventual occurrence of the above operations, a particular update to a particular field made by one thread will eventually be visible to another. But eventually can be an arbitrarily long time. Long stretches of code in threads that use no synchronization can be hopelessly out of synch with other threads with respect to values of fields. In particular, it is always wrong to write loops waiting for values written by other threads unless the fields are volatile or accessed via synchronization 个人想法:重点在于那个while循环,你无非是想测试在innerrun线程里是否看得到innerstop线程对stop变量的更改,目前你的方式是while循环不断去获取,这个死循环可能导致stop变量一直没有得到刷新(从主存到线程工作内存),你可以换一种放肆来测试:

class InnerRun extends Thread {
    boolean stop;
    StringBuffer sb = new StringBuffer();
    Object lock = new Object();
    int i = 0;

    @Override
    public void run() {
        System.out.println(stop);
        
        long start = System.currentTimeMillis();
        while (System.currentTimeMillis() - start < 2000) {  // 这个时间要大于主线程的睡眠时间
            
        }
        
        System.out.println(stop); // 打印出来的是true,当然理论上也不是一定打印true,因为我们说的保证可见性指的是立即可见,但是即使我们不刻意的去保证可见性,它其实最终也是可见的,只不过这个最终的时长是不确定(但往往短到你感觉不出来)而已,实际的运行往往很难测出这种情况
    }

    public void stopMe() {
        stop = true;
    }
}
总的来说,while死循环使得stop变量没有机会得到更新 另外,你的这个程序的逻辑就不太对(或许你是刻意这么写的。。),你要保证的是stop变量的可见性,你应该在读写stop变量时加synchronize或者用volatile修饰stop,而不是对i变量的操作加同步
1.加synchronize或volatile 肯定可以保证同步的。现在我是为了探索在不给变量枷锁的情况下,变量的同步策略。 2.“死循环可能导致stop变量一直没有得到刷新“?为什么?我觉得是因为stop变量没有刷新导致一直死循环 3,我的目录是在没有给变量枷锁的情况下,什么情况下可以看到stop线程的变量?是随机还是什么?你贴出的一段英文是Doug Lea的论文观点,我也是看了他的这个,有点疑惑。我们都知道:读线程要想拿到stop线程变量必须有两步:1,stop线程刷新变量到主内存,2.读线程强制从主内存读取。根据Doug Lea 的观点,我的代码: public void run() { while(!stop){ synchronized(lock){ for(int i=1;i<100;i++){ if (Math.log10(i) < 0) throw new AssertionError(); } } } } 为什么就能读取到stop线程的值,而去掉syncronized就读取不到 [/quote] 我觉得这个和synchronize没有太大关系,即使这么写:

public void run() {
while(!stop){
Thread.sleep(1);
}
}
} 
这个循环也是能结束的。 我猜测是不是有这样的可能:在循环里读取变量时线程总是从工作内存读,
引用 7 楼 zjy7554 的回复:
[quote=引用 6 楼 a1282379904 的回复:] Changes to fields made by one thread are guaranteed to be visible to other threads only under the following conditions: 1.A writing thread releases a synchronization lock and a reading thread subsequently acquires that same synchronization lock.In essence, releasing a lock forces a flush of all writes from working memory employed by the thread, and acquiring a lock forces a (re)load of the values of accessible fields 2.As a thread terminates, all written variables are flushed to main memory. For example, if one thread synchronizes on the termination of another thread using Thread.join, then it is guaranteed to see the effects made by that thread The memory model guarantees that, given the eventual occurrence of the above operations, a particular update to a particular field made by one thread will eventually be visible to another. But eventually can be an arbitrarily long time. Long stretches of code in threads that use no synchronization can be hopelessly out of synch with other threads with respect to values of fields. In particular, it is always wrong to write loops waiting for values written by other threads unless the fields are volatile or accessed via synchronization 个人想法:重点在于那个while循环,你无非是想测试在innerrun线程里是否看得到innerstop线程对stop变量的更改,目前你的方式是while循环不断去获取,这个死循环可能导致stop变量一直没有得到刷新(从主存到线程工作内存),你可以换一种放肆来测试:

class InnerRun extends Thread {
    boolean stop;
    StringBuffer sb = new StringBuffer();
    Object lock = new Object();
    int i = 0;

    @Override
    public void run() {
        System.out.println(stop);
        
        long start = System.currentTimeMillis();
        while (System.currentTimeMillis() - start < 2000) {  // 这个时间要大于主线程的睡眠时间
            
        }
        
        System.out.println(stop); // 打印出来的是true,当然理论上也不是一定打印true,因为我们说的保证可见性指的是立即可见,但是即使我们不刻意的去保证可见性,它其实最终也是可见的,只不过这个最终的时长是不确定(但往往短到你感觉不出来)而已,实际的运行往往很难测出这种情况
    }

    public void stopMe() {
        stop = true;
    }
}
总的来说,while死循环使得stop变量没有机会得到更新 另外,你的这个程序的逻辑就不太对(或许你是刻意这么写的。。),你要保证的是stop变量的可见性,你应该在读写stop变量时加synchronize或者用volatile修饰stop,而不是对i变量的操作加同步
1.加synchronize或volatile 肯定可以保证同步的。现在我是为了探索在不给变量枷锁的情况下,变量的同步策略。 2.“死循环可能导致stop变量一直没有得到刷新“?为什么?我觉得是因为stop变量没有刷新导致一直死循环 3,我的目录是在没有给变量枷锁的情况下,什么情况下可以看到stop线程的变量?是随机还是什么?你贴出的一段英文是Doug Lea的论文观点,我也是看了他的这个,有点疑惑。我们都知道:读线程要想拿到stop线程变量必须有两步:1,stop线程刷新变量到主内存,2.读线程强制从主内存读取。根据Doug Lea 的观点,我的代码: public void run() { while(!stop){ synchronized(lock){ for(int i=1;i<100;i++){ if (Math.log10(i) < 0) throw new AssertionError(); } } } } 为什么就能读取到stop线程的值,而去掉syncronized就读取不到 [/quote] 看下这个链接 https://help.semmle.com/wiki/display/JAVA/Spin+on+field
zjy7554 2018-04-14
  • 打赏
  • 举报
回复
引用 6 楼 a1282379904 的回复:
Changes to fields made by one thread are guaranteed to be visible to other threads only under the following conditions: 1.A writing thread releases a synchronization lock and a reading thread subsequently acquires that same synchronization lock.In essence, releasing a lock forces a flush of all writes from working memory employed by the thread, and acquiring a lock forces a (re)load of the values of accessible fields 2.As a thread terminates, all written variables are flushed to main memory. For example, if one thread synchronizes on the termination of another thread using Thread.join, then it is guaranteed to see the effects made by that thread The memory model guarantees that, given the eventual occurrence of the above operations, a particular update to a particular field made by one thread will eventually be visible to another. But eventually can be an arbitrarily long time. Long stretches of code in threads that use no synchronization can be hopelessly out of synch with other threads with respect to values of fields. In particular, it is always wrong to write loops waiting for values written by other threads unless the fields are volatile or accessed via synchronization 个人想法:重点在于那个while循环,你无非是想测试在innerrun线程里是否看得到innerstop线程对stop变量的更改,目前你的方式是while循环不断去获取,这个死循环可能导致stop变量一直没有得到刷新(从主存到线程工作内存),你可以换一种放肆来测试:

class InnerRun extends Thread {
    boolean stop;
    StringBuffer sb = new StringBuffer();
    Object lock = new Object();
    int i = 0;

    @Override
    public void run() {
        System.out.println(stop);
        
        long start = System.currentTimeMillis();
        while (System.currentTimeMillis() - start < 2000) {  // 这个时间要大于主线程的睡眠时间
            
        }
        
        System.out.println(stop); // 打印出来的是true,当然理论上也不是一定打印true,因为我们说的保证可见性指的是立即可见,但是即使我们不刻意的去保证可见性,它其实最终也是可见的,只不过这个最终的时长是不确定(但往往短到你感觉不出来)而已,实际的运行往往很难测出这种情况
    }

    public void stopMe() {
        stop = true;
    }
}
总的来说,while死循环使得stop变量没有机会得到更新 另外,你的这个程序的逻辑就不太对(或许你是刻意这么写的。。),你要保证的是stop变量的可见性,你应该在读写stop变量时加synchronize或者用volatile修饰stop,而不是对i变量的操作加同步
1.加synchronize或volatile 肯定可以保证同步的。现在我是为了探索在不给变量枷锁的情况下,变量的同步策略。 2.“死循环可能导致stop变量一直没有得到刷新“?为什么?我觉得是因为stop变量没有刷新导致一直死循环 3,我的目录是在没有给变量枷锁的情况下,什么情况下可以看到stop线程的变量?是随机还是什么?你贴出的一段英文是Doug Lea的论文观点,我也是看了他的这个,有点疑惑。我们都知道:读线程要想拿到stop线程变量必须有两步:1,stop线程刷新变量到主内存,2.读线程强制从主内存读取。根据Doug Lea 的观点,我的代码: public void run() { while(!stop){ synchronized(lock){ for(int i=1;i<100;i++){ if (Math.log10(i) < 0) throw new AssertionError(); } } } } 为什么就能读取到stop线程的值,而去掉syncronized就读取不到
zjy7554 2018-04-13
  • 打赏
  • 举报
回复
引用 1 楼 mj280824108 的回复:
原因是你开启线程和结束线程的间隔时间太短了,100毫秒你知道是什么概念么,也就是十分一秒就会执行结束程序,差不多是前后脚,你把时间改长些就明白问题所在了。
和这个时间没关系吧,我改成5秒10秒都是一样的结果
a1282379904 2018-04-13
  • 打赏
  • 举报
回复
Changes to fields made by one thread are guaranteed to be visible to other threads only under the following conditions: 1.A writing thread releases a synchronization lock and a reading thread subsequently acquires that same synchronization lock.In essence, releasing a lock forces a flush of all writes from working memory employed by the thread, and acquiring a lock forces a (re)load of the values of accessible fields 2.As a thread terminates, all written variables are flushed to main memory. For example, if one thread synchronizes on the termination of another thread using Thread.join, then it is guaranteed to see the effects made by that thread The memory model guarantees that, given the eventual occurrence of the above operations, a particular update to a particular field made by one thread will eventually be visible to another. But eventually can be an arbitrarily long time. Long stretches of code in threads that use no synchronization can be hopelessly out of synch with other threads with respect to values of fields. In particular, it is always wrong to write loops waiting for values written by other threads unless the fields are volatile or accessed via synchronization 个人想法:重点在于那个while循环,你无非是想测试在innerrun线程里是否看得到innerstop线程对stop变量的更改,目前你的方式是while循环不断去获取,这个死循环可能导致stop变量一直没有得到刷新(从主存到线程工作内存),你可以换一种放肆来测试:

class InnerRun extends Thread {
    boolean stop;
    StringBuffer sb = new StringBuffer();
    Object lock = new Object();
    int i = 0;

    @Override
    public void run() {
        System.out.println(stop);
        
        long start = System.currentTimeMillis();
        while (System.currentTimeMillis() - start < 2000) {  // 这个时间要大于主线程的睡眠时间
            
        }
        
        System.out.println(stop); // 打印出来的是true,当然理论上也不是一定打印true,因为我们说的保证可见性指的是立即可见,但是即使我们不刻意的去保证可见性,它其实最终也是可见的,只不过这个最终的时长是不确定(但往往短到你感觉不出来)而已,实际的运行往往很难测出这种情况
    }

    public void stopMe() {
        stop = true;
    }
}
总的来说,while死循环使得stop变量没有机会得到更新 另外,你的这个程序的逻辑就不太对(或许你是刻意这么写的。。),你要保证的是stop变量的可见性,你应该在读写stop变量时加synchronize或者用volatile修饰stop,而不是对i变量的操作加同步
zjy7554 2018-04-12
  • 打赏
  • 举报
回复
@Enchanter, 和这个时间没关系吧,我改成5秒10秒都是一样的结果
maradona1984 2018-04-12
  • 打赏
  • 举报
回复
亲测没注释也能结束程序
oyljerry 2018-04-11
  • 打赏
  • 举报
回复
只是巧合或者说时间点出现了恰巧的结果
Enchanter, 2018-04-11
  • 打赏
  • 举报
回复
原因是你开启线程和结束线程的间隔时间太短了,100毫秒你知道是什么概念么,也就是十分一秒就会执行结束程序,差不多是前后脚,你把时间改长些就明白问题所在了。

67,513

社区成员

发帖
与我相关
我的任务
社区描述
J2EE只是Java企业应用。我们需要一个跨J2SE/WEB/EJB的微容器,保护我们的业务核心组件(中间件),以延续它的生命力,而不是依赖J2SE/J2EE版本。
社区管理员
  • Java EE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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