在下面的示例中
private Integer a = 10;
private Integer c = -10;
private int b = -10;
public void acquire(){
synchronized(a){
print("acquire()");
try{
b = -12;
//Thread.sleep(5000);
a.wait(5000);
print("acquire : "+b);
print("I have awoken");
}catch(Exception e){
e.printStackTrace();
}
}
print("Leaving acquire()");
}
public void modify(int n){
print("Entered in modfy");
synchronized(a){
try{
b = -15;
//a.wait(5000);
Thread.sleep(5000);
print("modfy : "+b);
}catch(Exception e){
e.printStackTrace();
}
}
}
线程A正在调用acquire(),线程B正在调用modify()。
由一个线程更改的b值对另一个线程可见。
- 螺纹A:b=-12;并等待
- 螺纹B:B=-15;打印b;出口
- 线程A:打印b的新值,即-15
我如何用上面的例子模拟对volatile的需求,然后在上面的例子中使用volatile。
如果您只是想模拟volatile
的需要,只需执行以下操作:
volatile int x = 10;
public void acquire()
{
x = 15;
print("acquire()");
}
public void modify(int n)
{
print("Entered in modfy");
x = -15;
}
//Let threadA call acquire() and threadB call modify()Then print x and see (there should not be any race conditions)
如果你想让它更有趣,可以添加更多的线程和方法,并尝试访问x
这里有一个示例
http://vanillajava.blogspot.com/2012/01/demonstrating-when-volatile-is-required.html
你可能会发现的问题有:;
- 虽然
volatile
提供了一些内存一致性保证,但不使用volatile并不能保证您会看到问题 - 在Sun/Oracle/OpenJDK JVM中,问题似乎不会发生,直到代码得到优化,即运行了10000次之后
- 这通常涉及到一种微妙的种族状况,即使是微小的事情也可能意味着你看不到问题
根据《实践中的并发》,volatile应该只用于标志类变量。(这是布尔值)。当新状态不依赖于变量的旧状态时,应使用Volatile。。
volatile布尔标志=true;
while(flag){..//做某事;
}
当其他线程可以修改标志变量时,将通知原始线程,因为该标志被标记为volatile。否则,循环可能会永远消失。