0 0

一个多线程共享变量的问题,请教下大家15

工作中遇到了这么一个问题,想请教下大家。伪码如下:

public class AAAAA{

    public boolean enable = true; // [b]非violate[/b]

    // 线程A执行
    public void methodA{
        while(enable){
            .........
        }
        system.out.println("----------------------");
    }

    // 线程B执行
    public void methodB{
        enable = false;
    }
}


前提:线程A和线程B一定都会执行code。
问题:线程A是否有可能会永远退不出循环?如果存在这种可能,请详细说明下原因,谢谢。

本人认为不会存在这种情况,但是有同事说存在这种可能,原因是JVM指令重排序优化。我查了些资料,没有找到充足的证据来证明此事,所以想在这里请教下各位大侠,先行谢过了。

问题补充:去掉活性失败的例子,如果MethodA的实现变更为
public void methodA{
    while(true){
       dosomething();
       if (enable){
          dosomething();
       }
       dosomething();
    }
}

那么enable也会永远为true吗?
2012年7月30日 00:13

15个答案 按时间排序 按投票排序

0 0

采纳的答案

看看effective java 中文版第二版 230页  如果没错的话 这个描述叫做火星失败 liveness failure 是因为 while(enable) 被jvm 优化成 if(!enable) {
while (true)
}  所以需要设置同步访问 或者是 在enable 前 加上 violate 这样 当enable变化时 这样他能保证任何一个线程在读取该域的时候能看见最新被修改的值

以上全是引用书中的话 若有不全的地方 请看书籍

2012年7月30日 12:18
0 0

当有多个线程同时来存取某一个对象时,就出现可见性问题.
为了保证变量的可见性,一般可以用锁、 synchronized关键字、 volatile关键字或直接设置为final ;

线程在MethodA 方法中,只是一次从寄存器读取进来,一直没有改变变量enable的值,一般不会主动更新主存的值到工作内存中,所以很有可能一直下去.

2012年7月31日 15:49
0 0

昨天可能没有理解你的意思。后来想了想,这里跟JVM指令重排没有关系的,如果有A线程和B线程同时操作AAAA,但是至于A会不会退出循环,跟A线程和B线程执行的具体情况而定,假如线程B已经执行完了methodB,而且修改的enable对线程A可见,那么A就退出循环了。
  加上volatile主要是保证修改值后对另外的线程可见。这样可以保证线程间的同步,但是并不意味着不加volatile就不能实现同步,只是发生的事件是一个概率事件。也就是说线程A同样可以拿到线程B已经修改的enable的值。

    最后的结论是:这里讨论线程A是否有可能会永远退不出循环意义不大。因为这个事件本身是不确定事件。只有线程B执行完methodB后且修改enable值对线程A可见,而线程A执行methodA,这样就退出循环。是一个概率事件。如果非要对你那个下结论的话我觉得"不可能",既然是概率事件当样本足够多的话总会发生的。

    希望我的回答能帮到你

2012年7月31日 08:03
0 0

public boolean enable = true你需要加上volatile修饰符,确保内存可见性

2012年7月30日 17:53
0 0

自己写个程序run一下,不就知道结果了么

2012年7月30日 16:51
0 0

跟“指令重排序优化”无关,两个线程读取的enable可能是不同的,为了优化执行变量可能在cpu缓存里有副本,cpu缓存也是多级的,再加上内存里的副本,不一定读到的是哪个

可以给 enable加 volatile修饰 一种弱强制同步

2012年7月30日 15:53
0 0

if (enable){
          dosomething();
       }

如果你这个dosomething显示执行methodB,然后将enable = true;
这样就可以实现线程A一直循环下去了

2012年7月30日 15:53
0 0

如果你的2个线程是共享一个AAAAA对象的话,那是必定会退出循环的,2个线程共享的是相同的对象,同一个内存地址,线程B如果改变了enable的地址值,那么线程A的调用的methodAwhile循环条件就变成了false,那就退出了循环。

2012年7月30日 14:52
0 0

建议你把java编程思想第21章看几遍 边看边实践 看到你懂为止 比任何回答都强

2012年7月30日 12:54
0 0

可能的,原因是enable不是同步即时的(即你说的非violate的),然后线程A就会看不到enable的变化,就一直不断的执行while代码

2012年7月30日 11:44
0 0

程序的本意是希望methodB中断methodA,但是,由于编译器判断在methodA里面没有修改过enable ,因此可能只执行一次对enable 到某寄存器的读操作,然后每次if判断都只使用这个寄存器里面的“enable 副本”,导致循环永远无法退出,如果将enable加上volatile修饰,则编译器保证对此变量的读写操作作都不会被优化(肯定执行),那么当methodB执行完后,methodA即可退出循环。

2012年7月30日 11:31
0 0

首先,你确定你同一个AAAAA的对象被A,B线程同时操作了,才会出现线程问题。
假如是的话,除非B线程没有执行到,那么可能会一直执行下去,基本上不太可能,线程B总会改变这个enable。

  但是假如说:methodA和methodB都加上同步锁,出现的可能性就极大了。

2012年7月30日 11:12
0 0

不会出现线程问题  enable是这个成员变量

2012年7月30日 10:18
0 0

多线程 静态字段,都可以修改

2012年7月30日 08:58
0 0

根据你的伪代码不会出现线程问题吧,你的enable是一个私有变量。
我不知道是不是没有理解你的意思。

2012年7月30日 08:37

相关推荐

Global site tag (gtag.js) - Google Analytics