多线程的知识点是一个庞大的体现,对此也是只知其一;不知其二。一直想系统的深刻的学习多线程的知识,奈何一直没有找到机会,好吧,其实就是懒。最近在项目中接触到一个多并发的项目,在项目中踩了无数的坑。在此下定决心作一个并发的学习笔记。html
当两个线程同时对一个共享可变变量进行操做时,例如:缓存
两个线程对变量i=1同时执行i++操做。执行完毕后i可能并不等于3而是等于2。由于i++不是原子性的操做,i++其实是有三个步骤安全
第一步:读取,从主内存中将i=1读取到本地内存中。多线程
第二步:修改,i自增。并发
第三部:写入,将i=2写会到缓存中。性能
因此当两个线程同时将i读取到工做内存中,并分别将变量i赋值为2。学习
原子性是指一个操做是不可中断的,要么所有执行成功要么所有执行失败,有着“同生共死”的感受。及时在多个线程一块儿执行的时候,一个操做一旦开始,就不会被其余线程所干扰。测试
可见性是指当一个线程修改了共享变量后,其余线程可以当即得知这个修改。为何要这样说?难道一个线程修改了共享变量其余线程不必定会当即得知这个变量的修改?没错事实确实如此。 简单的举一个例子。 spa
数据 i 是存储在主内存中的,当一个线程执行 i++ 操做的时候首先将 i 从主内存读取到本身线程的工做内存中(也就是缓冲行),而后将工做内存的 i 执行+1操做。若是是单线程程序,在没有其余写入操做的状况下读取这个值,首先会读取缓冲行,缓存命中。那么总能获得 +1 操做以后的值。操作系统
可是多线程环境结果则会违背咱们的直觉。
因为操做系统的执行,咱们并不知道工做内存中的值什么时候才能被写入到主内存中(理由很简单,咱们不可能每次修改了缓存,操做系统就会将值瞬间刷入到主内存吧?这样效率会多低呀)。因此若是这以前另外一个线程从主内存读取 i 的值到本地工做内存中。那么他可能并不会感知到另外一个线程其实已经修改了 i 的值。
为何synchronized和volatile能够实现可见性咱们在后续会继续介绍。
在执行程序时,为了提升性能,编译器和处理器经常会对指令作重排序。
为何要进行重排序?
好比三个操做之间是没有逻辑关系的,那么是一个cpu串行执行三个操做快仍是将三个操做分别给三个cpu同时执行快呢?答案显而易见。
可是带来的一个弊端就是,可能代码的执行顺序与咱们的意愿相违背。
如何让程序具有有序性,咱们在后续会继续介绍。
1.不在线程之间共享该状态变量。
2.将状态变量修改成不可变的变量。
3.在访问状态变量时使用同步。
在介绍原理以前咱们须要了解什么是CAS自旋转,CAS自旋也就是咱们常说的乐观锁,他不会发生线程阻塞,当咱们将修改后的共享变量写回内存的时候,会检查在此期间这个共享变量是否被别的线程操做,若是被别的线程操做了,那么就回写内存失败,从新执行代码。(这样的好处在于对于同步块执行时间较短,上下文切换的代价是很是大的)
锁一共有4个状态,级别从低到高依次是:无所状态、偏向锁状态、轻量级锁状态和重量级锁状态,这几个状态会随着竞争逐渐升级,可是不能降级。而且这4个状态是存储在对象头中的。对象头中的Mark Word信息以下图所示(每一行表明一个状态)
在博客中发现一个大佬画的图仍是蛮详细的,你们能够参考参考
原文出处:https://www.cnblogs.com/zhxiansheng/p/10611994.html