引言

多线程的知识点是一个庞大的体现,对此也是一知半解。一直想系统的深入的学习多线程的知识,奈何一直没有找到机会,好吧,其实就是懒。最近在项目中接触到一个多并发的项目,在项目中踩了无数的坑。在此下定决心做一个并发的学习笔记。

为什么并发会有安全问题

当两个线程同时对一个共享可变变量进行操作时,例如:

两个线程对变量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信息如下图所示(每一行代表一个状态)

  1. 一个对象刚创建的时候是无锁状态,当第一个线程a打算访问同步块应获取锁的时候,会检查是否偏向锁,发现此时为0,则使用CAS操作将Mark Word中的来线程ID设置为自己的的线程ID。然后将是否偏向锁设置为1。以后该线程在进入和退出同步块的时候不需要进行CAS操作来加锁和解锁,只需要简单的测试一下线程ID是否指向自己即可。
  2. 当另一个线程b打算访问同步块,此时与之前的线程a发生竞争,此时就要执行偏向锁的撤销。首先发出暂停线程a的指令,如果线程a还存活并且在代码块中。那么线程a在执行到安全点的时候会安全退出。
    线程b检查是否存活或者是否已经退出同步块:

    • 如果不存活或者已退出同步块则将对像头设置为无锁状态(也就是将是否偏向锁设置为0)。然后重复步骤一,使用CAS操作将Mark Word中的来线程ID设置为自己的的线程ID。
    • 如果存活并且在还在同步块当中,则将锁升级为轻量级锁。
  3. 轻量级锁轻量级锁是相对于重量级锁而言的。使用轻量级锁时,不需要申请互斥量,仅仅将Mark Word中的部分字节CAS更新指向线程栈中的Lock Record,如果更新成功,则轻量级锁获取成功,记录锁状态为轻量级锁;如果CAS更新失败,说明已经有线程获得了轻量级锁,目前发生了锁竞争(不适合继续使用轻量级锁),接下来膨胀为重量级锁。
  4. 重量级别的锁底层直接与操作系统打交道,也就是我们平常说的阻塞,线程会发生阻塞,当竞争线程释放锁的时候,才会唤醒阻塞线程。

在博客中发现一个大佬画的图还是蛮详细的,大家可以参考参考

参考:浅谈偏向锁、轻量级锁、重量级锁

java并发编程(1) --并发基础及其锁的原理的更多相关文章

  1. Java并发编程系列-(4) 显式锁与AQS

    4 显示锁和AQS 4.1 Lock接口 核心方法 Java在java.util.concurrent.locks包中提供了一系列的显示锁类,其中最基础的就是Lock接口,该接口提供了几个常见的锁相关 ...

  2. Java并发编程(七)ConcurrentLinkedQueue的实现原理和源码分析

    相关文章 Java并发编程(一)线程定义.状态和属性 Java并发编程(二)同步 Java并发编程(三)volatile域 Java并发编程(四)Java内存模型 Java并发编程(五)Concurr ...

  3. Java并发编程系列-(8) JMM和底层实现原理

    8. JMM和底层实现原理 8.1 线程间的通信与同步 线程之间的通信 线程的通信是指线程之间以何种机制来交换信息.在编程中,线程之间的通信机制有两种,共享内存和消息传递. 在共享内存的并发模型里,线 ...

  4. Java并发编程:并发容器之CopyOnWriteArrayList(转载)

    Java并发编程:并发容器之CopyOnWriteArrayList(转载) 原文链接: http://ifeve.com/java-copy-on-write/ Copy-On-Write简称COW ...

  5. Java并发编程:并发容器之ConcurrentHashMap(转载)

    Java并发编程:并发容器之ConcurrentHashMap(转载) 下面这部分内容转载自: http://www.haogongju.net/art/2350374 JDK5中添加了新的concu ...

  6. Java并发编程:并发容器之ConcurrentHashMap

    转载: Java并发编程:并发容器之ConcurrentHashMap JDK5中添加了新的concurrent包,相对同步容器而言,并发容器通过一些机制改进了并发性能.因为同步容器将所有对容器状态的 ...

  7. Java并发编程:并发容器之CopyOnWriteArrayList

    转载: Java并发编程:并发容器之CopyOnWriteArrayList Copy-On-Write简称COW,是一种用于程序设计中的优化策略.其基本思路是,从一开始大家都在共享同一个内容,当某个 ...

  8. Java并发编程:并发容器ConcurrentHashMap

    Java并发编程:并发容器之ConcurrentHashMap(转载) 下面这部分内容转载自: http://www.haogongju.net/art/2350374 JDK5中添加了新的concu ...

  9. 【Java并发编程】并发编程大合集-值得收藏

    http://blog.csdn.net/ns_code/article/details/17539599这个博主的关于java并发编程系列很不错,值得收藏. 为了方便各位网友学习以及方便自己复习之用 ...

  10. 【转】Java并发编程:并发容器之CopyOnWriteArrayList

    Copy-On-Write简称COW,是一种用于程序设计中的优化策略.其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容Copy出去形成一个新的内容然后再改, ...

随机推荐

  1. 快速开发框架,及库存管理系统,基于easyui框架和C#语言MVC、EntityFrameWork、T4模板技术。

    快速开发框架,及库存管理系统,基于easyui框架和C#语言MVC.EntityFrameWork.T4模板技术. 产品界面如下图所示: 源码结构: 开放全部源码,如有需要请联系,QQ:1107141 ...

  2. 学习了解CyclicBarrier

    CyclicBarrier我的理解就是一个线程等待器,用途就是将注册了这个barrier的线程卡在同一个位置,直到注册这个barrier的所有线程都完成之后,继续执行.下面是一个学习过程中采用的示例, ...

  3. tomcat7性能调优与配置(以windows版为例)

    一.配置tomcat服务状态查看帐号(E:\Tomcats\apache-tomcat-7.0.73Test\conf下面的tomcat-users.xml中)加入:<user username ...

  4. Socket 异步通信

    最近在写数据通信的时候用到的东西!希望对大家有帮助 /// <summary> /// 获取或设置服务器IP地址 /// </summary> public string se ...

  5. Win10 UWP开发系列:解决Win10不同版本的Style差异导致的兼容性问题

    最近在开发一个项目时,遇到了一个奇怪的问题,项目依赖的最低版本是10586,目标版本是14393,开发完毕发布到商店后,很多用户报无法正常加载页面.经查,有问题的都是Win10 10586版本. 我上 ...

  6. struts2中的拦截器

    一  AOP思想: 面向切面编程的思想 AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP ...

  7. CentOS6.7 mysql5.6.33修改数据文件位置

    问题:mysql存放的数据文件,分区容量较小,目前已经满,导致mysql连接不上, 解决方案: 1.删除分区里一个不需要用的数据,如:日志文件等(解决不了根本问题) 2.对某个磁盘扩容 3.修改数据存 ...

  8. Spring Security 源码分析(四):Spring Social实现微信社交登录

    社交登录又称作社会化登录(Social Login),是指网站的用户可以使用腾讯QQ.人人网.开心网.新浪微博.搜狐微博.腾讯微博.淘宝.豆瓣.MSN.Google等社会化媒体账号登录该网站. 前言 ...

  9. 你不知道的JavaScript--Item29 DOM基础详解

    看完JavaScript高级程序设计,整理了一下里面的DOM这一块的知识点,比较多,比较碎!DOM在整个页面的地位如图: DOM(文档对象模型)是针对HTML 和XML 文档的一个API(应用程序编程 ...

  10. scrapy分布式爬虫scrapy_redis一篇

    分布式爬虫原理 首先我们来看一下scrapy的单机架构:     可以看到,scrapy单机模式,通过一个scrapy引擎通过一个调度器,将Requests队列中的request请求发给下载器,进行页 ...