关于 volatile 修饰在数组上的可见性问题

chenchengamao 2014-12-01 01:16:34
使用 volatile 修饰符时,大家都知道被它修饰的属性都有可见性的特性,如果将 volatile 修饰在数组上,按我的理解是:它只对引用这个数组的属性本身有效,而对这个数组里面引用的对象,是没有效果的。但是现在问题来了,我在做试验时,发现在它对数组里面引用的对象也有效果,我的代码如下,大家帮忙看看是什么原因。



代码一:

public class Test {

private String name;

public Test(String name) {
this.name = name;
}

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}

}



代码二:

public class MyJava {

public static final int SIZE = 1000;
private volatile Test[] testArray = new Test[SIZE];

public MyJava() {
for (int i = 0; i < SIZE; i++) {
testArray[i] = new Test("name:" + i);
}
}
public static void main(String[] args) {
new InitThread(new MyJava()).start();
}
public Test getTest(int index) {
return testArray[index];
}
public void setTest(int index, Test t) {
testArray[index] = t;
}

}



代码三:

import java.util.Random;

public class InitThread extends Thread {

private MyJava mj;

public InitThread(MyJava mj) {
this.mj = mj;
}

public void run() {
int at = new Random().nextInt(MyJava.SIZE);
for (int i = 0; i < 10; i++) {
new MonitorThread("线程:" + i, mj, at).start();
}
try {
Thread.sleep(3000);
} catch (Exception e) {
}
Test t = mj.getTest(at);
t.setName("测试");
}

}



代码四:

public class MonitorThread extends Thread {

private MyJava mj;
private int at;

public MonitorThread(String name, MyJava mj, int at) {
super(name);
this.mj = mj;
this.at = at;
}

public void run() {
while (true) {
Test t = mj.getTest(at);
if ("测试".equals(t.getName())) break;
}
System.out.println(getName() + " over");
}

}




测试结果:
在代码二中,如果 testArray 属性有加 volatile ,那么代码四的线程会执行完,如果把 testArray 属性的 volatile 拿掉,那么代码四的线程就永远执行不完。
按道理说 testArray 属性不讨是有加 volatile 还是没加 volatile ,代码四都应该永远执行不完才对,现在变成有加 volatile 会执行完,这是什么原因?



...全文
484 1 打赏 收藏 转发到动态 举报
写回复
用AI写文章
1 条回复
切换为时间正序
请发表友善的回复…
发表回复
wyc_ 2014-12-01
  • 打赏
  • 举报
回复
volatile修饰的变量一旦被修改会立刻刷入主存,因此其他线程可见。 这里如果去掉volatile,MonitorThread可能在t.setName("测试");之前(或者尽管执行了setName但是还未刷到主存)读取testArray放到自身线程的工作栈中,所以看到的是过期的值。可以把MonitorThread的run方法改为如下形式: public void run() { try { Thread.sleep(5000); } catch (Exception e) { } while (true) { 。。。 } } 这时InitThread线程可以在MonitorThread读取之前执行t.setName("测试");(并有更大的希望刷新到主存)因此MonitorThread可以读取到最新的值而能顺利结束。 本人菜鸟,希望大神指正

62,614

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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