问题
之前有小伙伴在星球提了这么一个问题
这篇文章时隔一两年,突然看到还是有点印象,文章中,我只是强硬的抛出了一个结论:虽然table变量被volatile修饰了,但里面的元素并没有volatile修饰,无法保证元素的可见性。
但是这种解释,似乎被众多的小伙伴怀疑,也一直没有找到有理的证据(基础还是太弱)
不过在星球中,大家还是很积极的讨论,虽然有些是错的(这种问题在大部分人身上都有,你不表现出来,那你永远不知道自己所掌握的东西其实是不对的)
解惑
在ConcurrentHashMap(1.8)中,内部使用一个volatile的数组table保存数据,细心的同学可以发现,Doug Lea每次在获取数组的元素时,采用Unsafe类的getObjectVolatile方法,在设置数组元素时,采用compareAndSwapObject方法,而不是直接通过下标去操作,这是为什么?
今天得到R大的确认:这个是因为Java数组在元素层面的元数据设计上的缺失,无法表达元素是final、volatile等语义,所以开了后门,使用getObjectVolatile用来补上无法表达元素是volatile的坑,@Stable用来补上final的坑,数组元素就跟没有标volatile的成员字段一样,无法保证线程之间可见性。
只有触发happens before关系的操作,才能保证线程之间的可见性,比如使用table[0] = new Object()直接赋值,这个赋值不会触发任何happens before关系的操作,相当于对一个无volatile变量进行赋值一样。
加入知识星球,不仅可以查漏补缺,还可以验证自己对Java的理解。
目前已有260+小伙伴加入。
网友评论
测试之后数组元素是volatile的
是我的测试方法有问题吗?
public class work2 {
public static void main(String[] args)
{
try
{
Test t = new Test();
t.start();
Thread.sleep(2000);
t.setRunning(false);
System.out.println("已赋值为false");
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
class Test extends Thread
{
private volatile boolean[] isRunning = new boolean[10];
public Test(){
isRunning[0]=true;
}
public void setRunning(boolean isRunning)
{
this.isRunning[0]=isRunning;
}
public void run()
{
System.out.println("进入run了");
while (isRunning[0]== true){};
System.out.println("线程被停止了");
}
}
引用这句话,请问一下如果是copyonwritearrayist,也不能保证每次get都是最新的元素吗,即便数组也同样被volatile修饰