java并发编程(1) --并发基础及其锁的原理
引言
多线程的知识点是一个庞大的体现,对此也是一知半解。一直想系统的深入的学习多线程的知识,奈何一直没有找到机会,好吧,其实就是懒。最近在项目中接触到一个多并发的项目,在项目中踩了无数的坑。在此下定决心做一个并发的学习笔记。
为什么并发会有安全问题
当两个线程同时对一个共享可变变量进行操作时,例如:
两个线程对变量i=1同时执行i++操作。执行完毕后i可能并不等于3而是等于2。因为i++不是原子性的操作,i++实际上是有三个步骤
第一步:读取,从主内存中将i=1读取到本地内存中。
第二步:修改,i自增。
第三部:写入,将i=2写会到缓存中。
所以当两个线程同时将i读取到工作内存中,并分别将变量i赋值为2。
原子性
原子性是指一个操作是不可中断的,要么全部执行成功要么全部执行失败,有着“同生共死”的感觉。及时在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程所干扰。
可见性
可见性是指当一个线程修改了共享变量后,其他线程能够立即得知这个修改。为什么要这样说?难道一个线程修改了共享变量其他线程不一定会立即得知这个变量的修改?没错事实确实如此。
简单的举一个例子。
数据 i 是存储在主内存中的,当一个线程执行 i++ 操作的时候首先将 i 从主内存读取到自己线程的工作内存中(也就是缓冲行),然后将工作内存的 i 执行+1操作。如果是单线程程序,在没有其他写入操作的情况下读取这个值,首先会读取缓冲行,缓存命中。那么总能得到 +1 操作之后的值。
但是多线程环境结果则会违背我们的直觉。
由于操作系统的执行,我们并不知道工作内存中的值何时才能被写入到主内存中(理由很简单,我们不可能每次修改了缓存,操作系统就会将值瞬间刷入到主内存吧?这样效率会多低呀)。所以如果这之前另一个线程从主内存读取 i 的值到本地工作内存中。那么他可能并不会感知到另一个线程其实已经修改了 i 的值。
为什么synchronized和volatile可以实现可见性我们在后续会继续介绍。
有序性
在执行程序时,为了提高性能,编译器和处理器常常会对指令做重排序。
为什么要进行重排序?
比如三个操作之间是没有逻辑关系的,那么是一个cpu串行执行三个操作快还是将三个操作分别给三个cpu同时执行快呢?答案显而易见。
但是带来的一个弊端就是,可能代码的执行顺序与我们的意愿相违背。
如何让程序具备有序性,我们在后续会继续介绍。
如何避免并发问题
1.不在线程之间共享该状态变量。
2.将状态变量修改为不可变的变量。
3.在访问状态变量时使用同步。
锁的原理
在介绍原理之前我们需要了解什么是CAS自旋转,CAS自旋也就是我们常说的乐观锁,他不会发生线程阻塞,当我们将修改后的共享变量写回内存的时候,会检查在此期间这个共享变量是否被别的线程操作,如果被别的线程操作了,那么就回写内存失败,重新执行代码。(这样的好处在于对于同步块执行时间较短,上下文切换的代价是非常大的)
锁一共有4个状态,级别从低到高依次是:无所状态、偏向锁状态、轻量级锁状态和重量级锁状态,这几个状态会随着竞争逐渐升级,但是不能降级。并且这4个状态是存储在对象头中的。对象头中的Mark Word信息如下图所示(每一行代表一个状态)
- 一个对象刚创建的时候是无锁状态,当第一个线程a打算访问同步块应获取锁的时候,会检查是否偏向锁,发现此时为0,则使用CAS操作将Mark Word中的来线程ID设置为自己的的线程ID。然后将是否偏向锁设置为1。以后该线程在进入和退出同步块的时候不需要进行CAS操作来加锁和解锁,只需要简单的测试一下线程ID是否指向自己即可。
- 当另一个线程b打算访问同步块,此时与之前的线程a发生竞争,此时就要执行偏向锁的撤销。首先发出暂停线程a的指令,如果线程a还存活并且在代码块中。那么线程a在执行到安全点的时候会安全退出。
线程b检查是否存活或者是否已经退出同步块:- 如果不存活或者已退出同步块则将对像头设置为无锁状态(也就是将是否偏向锁设置为0)。然后重复步骤一,使用CAS操作将Mark Word中的来线程ID设置为自己的的线程ID。
- 如果存活并且在还在同步块当中,则将锁升级为轻量级锁。
- 轻量级锁轻量级锁是相对于重量级锁而言的。使用轻量级锁时,不需要申请互斥量,仅仅将Mark Word中的部分字节CAS更新指向线程栈中的Lock Record,如果更新成功,则轻量级锁获取成功,记录锁状态为轻量级锁;如果CAS更新失败,说明已经有线程获得了轻量级锁,目前发生了锁竞争(不适合继续使用轻量级锁),接下来膨胀为重量级锁。
- 重量级别的锁底层直接与操作系统打交道,也就是我们平常说的阻塞,线程会发生阻塞,当竞争线程释放锁的时候,才会唤醒阻塞线程。
在博客中发现一个大佬画的图还是蛮详细的,大家可以参考参考
java并发编程(1) --并发基础及其锁的原理的更多相关文章
- Java并发编程系列-(4) 显式锁与AQS
4 显示锁和AQS 4.1 Lock接口 核心方法 Java在java.util.concurrent.locks包中提供了一系列的显示锁类,其中最基础的就是Lock接口,该接口提供了几个常见的锁相关 ...
- Java并发编程(七)ConcurrentLinkedQueue的实现原理和源码分析
相关文章 Java并发编程(一)线程定义.状态和属性 Java并发编程(二)同步 Java并发编程(三)volatile域 Java并发编程(四)Java内存模型 Java并发编程(五)Concurr ...
- Java并发编程系列-(8) JMM和底层实现原理
8. JMM和底层实现原理 8.1 线程间的通信与同步 线程之间的通信 线程的通信是指线程之间以何种机制来交换信息.在编程中,线程之间的通信机制有两种,共享内存和消息传递. 在共享内存的并发模型里,线 ...
- Java并发编程:并发容器之CopyOnWriteArrayList(转载)
Java并发编程:并发容器之CopyOnWriteArrayList(转载) 原文链接: http://ifeve.com/java-copy-on-write/ Copy-On-Write简称COW ...
- Java并发编程:并发容器之ConcurrentHashMap(转载)
Java并发编程:并发容器之ConcurrentHashMap(转载) 下面这部分内容转载自: http://www.haogongju.net/art/2350374 JDK5中添加了新的concu ...
- Java并发编程:并发容器之ConcurrentHashMap
转载: Java并发编程:并发容器之ConcurrentHashMap JDK5中添加了新的concurrent包,相对同步容器而言,并发容器通过一些机制改进了并发性能.因为同步容器将所有对容器状态的 ...
- Java并发编程:并发容器之CopyOnWriteArrayList
转载: Java并发编程:并发容器之CopyOnWriteArrayList Copy-On-Write简称COW,是一种用于程序设计中的优化策略.其基本思路是,从一开始大家都在共享同一个内容,当某个 ...
- Java并发编程:并发容器ConcurrentHashMap
Java并发编程:并发容器之ConcurrentHashMap(转载) 下面这部分内容转载自: http://www.haogongju.net/art/2350374 JDK5中添加了新的concu ...
- 【Java并发编程】并发编程大合集-值得收藏
http://blog.csdn.net/ns_code/article/details/17539599这个博主的关于java并发编程系列很不错,值得收藏. 为了方便各位网友学习以及方便自己复习之用 ...
- 【转】Java并发编程:并发容器之CopyOnWriteArrayList
Copy-On-Write简称COW,是一种用于程序设计中的优化策略.其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容Copy出去形成一个新的内容然后再改, ...
随机推荐
- Flask入门之Pycharm写Hello Word
在这里记录整理Flask入门的笔记! 今天讲讲使用Pycharm写一个Hello World 前提是已经安装好Pycharm,安装过程很简单,这里不赘述. 只放两个可以提供注册码的网站: Intell ...
- EF Core Model更新迁移
EF Core 迁移 感觉就是以前EF Code First的自动同步数据库功能 内容:在你新增.更新TableModel后,如何自动化的更新DB中的真实Table.以及对这些更改进行一个版本控制. ...
- 洛谷 P1613 解题报告
P1613 跑路 题目描述 小\(A\)的工作不仅繁琐,更有苛刻的规定,要求小\(A\)每天早上在\(6:00\)之前到达公司,否则这个月工资清零.可是小\(A\)偏偏又有赖床的坏毛病.于是为了保住自 ...
- redux 中间件 --- applyMiddleware 源码解析 + 中间件的实战
前传 中间件的由来 redux的操作的过程,用户操作的时候,我们通过dispatch分发一个action,纯函数reducer检测到该操作,并根据action的type属性,进行相应的运算,返回st ...
- 微信小程序基于第三方websocket的服务器端部署
微信小程序后台请求越来越严格 1.request要求用https 2.websocket要求用wss 3.测试后发现websocket只能走433端口 作为.net开发,websocket又是使用的第 ...
- Tar专题
下面的脚本根据当前的系统时间生成压缩文件名,并备份文件到指定目录: DIR=/www/webbackup/web/ FILE_NAME=`date +%y%m%d%H` FILE_NAME=$DIR/ ...
- 「JavaScript」JS四种跨域方式详解
原文地址https://segmentfault.com/a/1190000003642057 超详细并且带 Demo 的 JavaScript 跨域指南来了! 本文基于你了解 JavaScript ...
- 详解vue生命周期
vue生命周期 @(vue)[生命周期] 前言 在使用vue一个多礼拜后,感觉现在还停留在初级阶段,虽然知道怎么和后端做数据交互,但是对于mounted这个挂载还不是很清楚的.放大之,对vue的生命周 ...
- HTML5 CSS3 专题 : 拖放 (Drag and Drop)
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/31413767 本来准备写一个支持多图片拖拽上传的例子,但是为了更好的理解,先介绍 ...
- python改变输出字体颜色==>colorama
colorama是python第三方库中一个可以改变输出流颜色的玩意儿, 安装可以通过: pip install colorama 简单介绍 from colorama import Fore, Ba ...