Java并发编程总结

volatile实质是内存的可见性:

cpu缓存包括:寄存器、L1、L2等,把线程的计算结果首先缓存在CPU缓存中,这对于线程来说是本地缓存,CPU不直接和内存进行通讯,只和本地缓存进行通信,当一个变量声明为volatile时,当对这个volatile变量进行读的时候,CPU会首先把自己的本地缓存中的内容失效,把内存中对应的数据加载到本地缓存,再进行操作。当对这个volatile变量进行写操作完成以后,cpu会把本地缓存的内容直接刷新会内存中,这样其他线程就可以读到最新的修改值了,所以通过volatile可以实现线程之间的通讯。volatile通过cpu的指令重排序、happen-before规则等保证volatile语义在JMM中的内存可见性。

锁的释放和获取

当获取锁的时候,cpu本地缓存失效,直接从内存中重新刷新对应的值,当释放锁的时候,cpu会把本地缓存中的内容立即刷新到内存中,这样多个线程获取的值都是处理过的最新值,从而保证了多线程的并发访问,因为当一个线程读取加锁的内容时,cpu首先失效本地缓存,相当于直接从内存获取对应的内容。

volatile相对于锁比较轻量级,但是volatile同样具有锁的意义。

锁的分类

1、偏向锁: 线程A获取锁处理完成临界区代码后释放锁,此时CPU唤醒就绪状态的多个线程,但是只有一个线程能够获取这把锁,假如A再次尝试获取锁,A首先会比较对象头中mark word中锁标志位是否为偏向锁,然后比较存放的线程标识是否为线程A是否相等,如果相等则直接获取了这把锁,避免了进行CAS比较线程上下文的切换,这些都会对CPU的性能进行开销。

2、轻量级锁:线程A获取锁处理完成临界区代码释放锁后,线程B从就绪状态被唤醒,线程B尝试获取锁,但是失败了,这时候线程B开始进行自旋,等待再次获取锁,而不是放弃。这个等待后获取锁为轻量级锁。

3、重量级锁:就是普通的线程A放弃锁,线程B获取锁,进行上下文切换。

4、公平锁:公平锁获取时、首先回去读volatile变量的状态

5、非公平锁:非公平锁获取是,首先会用CAS更新volatile变量,这个操作同时具有volatile读和volatile写的内存语义

6、可重入锁:

CAS 是 compare and set,是一个native方法,不同CPU型号处理不一样,会有lock的执行,intel的手册中对lock的说明如下:

1、确保对内存的读-改-写的原子操作。在Pentium及Pentium之前的处理器中,带有lock前缀的指令在执行期间会锁住总线,使得其他处理器暂时无法通过总线访问内存。很显然,这会带来昂贵的开销。从Pentium4、IntelXeon及P6处理器开始,Intel使用锁定caceh 来保证指令的执行原子性,大大降低了lock的开销。
2、禁止该指令,与之前和之后的读写指令重排序
3、把写缓冲区中的所有数据刷新到内存中。
上面的两点足以同事实现volatile读和volatile写的内存语义。

现在对公平锁和非公平锁的内存语义做个总结:

1、公平锁和非公平锁释放时,最后都要写一个volatile变量的state
2、公平锁获取时,首先 会去读volatile变量
3、非公平锁获取时,首先会用CAS更新volatile变量,这个操作同时具有volatile的读和写的语义。

线程之间通讯的四种方式

1、A线程写volatile变量,随后B线程读取这个volatile变量
2、A线程写volatile变量,随后B线程用CAS更新这个volatile变量
3、A线程用CAS更新一个volatile变量,随后B线程用CAS更新这个volatile变量
4、A线程用CAS更新一个volatile变量,随后B线程读这个volatile变量

你可能感兴趣的:(Java并发编程总结)