前言
今天回家火车上没事情干,一直在看石杉码农老大的技术博客。其中看了些并发系列的文章,虽然都是之前学习JDK源码的时候都了解到的东西,可是隔久了发现本身不是很清晰了,因此到家后把本身笔记捞出来复习一遍,把知识点再串联一次,让本身理解更深入一点。缓存
并发问题
并发问题是指多线程读写共享内存的时候,产生结果不可预期的状况,并发问题的产生的缘由能够归结为两种,一是由于竞态条件,二是由于内存可见性、多线程
竞态条件
什么是竞态条件
竞态条件,官方解释是若是程序运行顺序的改变会影响最终结果,这就是一个竞态条件。并发
这句话有点抽象,描述的有点抽象,我我的对竞态条件的理解是多个线程在竞争同一系列资源的使用权,由于使用都是有时间的,不是咔嚓一下完成的,对于每一个线程来讲它可以正确执行的条件就是要求从获取资源到使用资源完成都是原子性的,不容许其余线程来中途改变资源从而影响了原子性,每一个线程都须要这种条件,我把这种多个线程对于共享资源获取到操做完成的原子性的需求叫作竞态条件。函数
针对竞态条件问题的应对思想
结合本身所学,总结一下应对竞态条件的思想:学习
- 经过路由避免竞争,这反映在并发包中有ConcurrentHashMap的分段锁机制、Java8对Cas的优化如LongAdder代替AtomicLong等等,不少地方都是运用了这种思想下降竞争资源粒度。
- 对资源的获取以及使用资源进行串行化,经过锁、CAS、队列来串行化获取资源和操做资源的操做。
- 写时复制避免读写竞争。好比ArrayList的写操做,没有必要在写的过程不让读,经过写时复制是能够同时进行读的。对应的并发容器CopyOnWriteArrayList就是采用的写时复制原理使得随时均可以读,相似的还有InnoDb的MVCC快照读、各类读缓存机制如eureka的多级缓存机制。等下讲到的JVM针对共享变量主内存、工做内存都是相似的思想。
内存可见性
什么是内存可见性
先看这张图:优化

如图,在Java内存模型中,对于共享变量data,两个工做线程都须要读取这个值的时候,其实是读取和操做的副本,相似高速本地缓存,这样作的缘由是使用缓存机制下降对data的读写并发冲突,否则都去读写同一个内存地址,效率是很低的。线程
可是这样形成的问题是,各个工做线程的变量不是即时同步的,如线程1将data改成了1,可是对于线程2来讲可能他本身的data副本中尚未同步为1,他读取的仍是0。这就是内存可见性问题。对象
怎么避免内存可见性致使的问题
- 一是使用synchronized同步锁,锁住共享数据。可是这种对于解决内存可见性来讲较重。
- 二是使用volatile关键字,使用volatile关键字修饰时,可理解为对数据的操做都在主存中进行,相比synchronized同步锁,volatile关键字更轻量级一点。另外volatile除了解决了内存可见性,也禁止指令重排(好比懒汉式单例,在初始化一个对象赋值给一个变量的过程,可能分为三步,第一步开辟内存空间,第二步调用构造函数初始化对象,第三步将变量指向分配的内存空间,当不加volatile关键字时没法保证这三步的顺序,因此多线程有可能对象不为Null可是变量不为null,可是却没有初始化,致使报错)。