想请教一下为什么 SynchronizedList 是读写均加锁,而 ConcurrentHashMap 只有写加了锁,读并没有加锁。
查询了相关资料,ConcurrentHashMap 读不加锁是因为 Node<K,V> 的 val 是用 volatile 声明的,因此保证了他的可见性,这里的 val 是一个对象。
但是我发现 SynchronizedList 中 list 是用 final 声明的, 而没有使用 volatile。
而根据查询到的资料说 volatile 声明对象或者数组,只能让其引用可见,那么他的元素/属性是如何保证可见呢?
自己做了一些简单的测试, 发现 volatile 声明的对象或者数组,他们的属性或者元素都是可见的(也有说是JIT优化产生的结果)如果 volatile 可以实现读的安全的话,SynchronizedList 是否可以使用 volatile 声明 list 而不给读加锁?
public static void main(String[] args) {
VolatileDemo volatileDemo = new VolatileDemo();
new Thread(() -> {
long start = System.currentTimeMillis();
System.out.println(start);
for (int i = 0; i < 1000; i++) {
try {
volatileDemo.put(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("end");
System.out.println(System.currentTimeMillis() - start);
}).start();
new Thread(() -> {
for (; ; ) {
volatileDemo.printNew();
}
}).start();
}
static class FlagClass{
boolean hasNew = false;
}
ConcurrentHashMap
是采用非阻塞算法实现的,就算是在写的情况下,一般也不会加锁。使用cas速度很快,就是实现相当复杂。SynchronizedList
就可以看成是一个普通的list,给方法都加上了同一个锁来保证安全,这样实现效率非常低。至于volatile 只能保证可见性,不能保证线程安全。就像你说的,如果放在引用上,只能保证引用的可见性。