Java 并发编程基础学习
1.Java 并发编程的容器
ConcurrentHashMap,HashTable,Collections.synchronizedMap 三者比较。
HashTable,Collections.synchronizedMap 提供对hashmap的独占锁,当有一个线程对其remove,add,遍历,等操作时,即使cpu有空闲的资源,也不能访问该容器。可伸缩性比较差。
而ConcurrentHashMap采用的颗粒更细小的锁,采用32个独立的锁对hash bucket的子集进行,那么最多就可以有32个线程进行并发访问。显然极大的提高了并发性,但是带来一个问题,如果有些操作必须使用独占访问,那该如何是好呢?那不是要一口气获取32个锁嘛?Brain Gates 说这种情况很少,如果遇到用递归的方法(疑问是如何递归?)
2.Synchronized和Volatile 的比较
Volatile 具备Synchronized 的可见性,但是不具备原子性。
使用Volatile满足的两个条件:
- 对变量的写操作不依赖于当前值。
- 该变量没有包含在具有其他变量的不变式中
为什么要使用Volatile?
Brain Gates 认为首先是易用性,其次是性能原因。Volatile的读操作的性能和没有Volatile相差无几。但是写操作开销就比没有Volatile 大很多,并且不会导致阻塞,伸缩性比Synchronized 好。
首先我们简单介绍下JMM(Java Memeory Model),JMM会利用寄存器和cache来提高对内存的访问速度,Java语言规范允许内存变量的改变对其他线程不一定可见。Java 语言规范里面我们知道,在没有同步的情况下, 一个给定线程中某种顺序的写操作对于另外一个不同的线程来说可能呈现出不同的顺序, 并且对内存变量的更新从一个线程传播到另外一个线程的时间是不可预测的。
使用同步后,如何实现确保可见性呢,通过执行内存屏障(Memory Barrier)当线程进入被同步的代码块,首先获取监视器,执行(Read Barrier)使得缓存在线程的局部内存全部实效,这将使得CPU 重新读取同步代码块的变量。当线程释放一个监视器,执行(Write Barrier)使得缓存在局部内存的修改过的变量写回主存。
我们知道同步的作用除了保证原子性之外,还提供可见性和顺序性。
使用Volatile 几种模式?
1.process的状态标识 2.一次性安全发布 3.独立观察 4.volatile Bean 模式 5开销较低的读写模式
参考文献:
Brain Gates Java 理论与实践: 正确使用 Volatile 变量
《Java 并发编程实战》