引言

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

为什么并发会有安全问题

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

两个线程对变量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. 使用TortoiseGit操作分支的创建与合并

    第一步:创建本地分支 点击右键选择TortoiseGit,选择Create Branch…,在Branch框中填写新分支的名称(若选中”switch to new branch”则直接转到新分支上,省 ...

  2. Pycharm快捷键记录

    这里只记录自己用过的,记录而已 会慢慢添加进来,没有考虑分类和顺序,后期足够多了会整理 参考文章: 1. pycharm的一些快捷键 2. pycharm快捷键及一些常用设置 Ctrl+C  直接复制 ...

  3. 终于解决文件格式问题 unix格式

    关于这个问题,今天终于找到方法  file-setting下 左侧code style  line separator下拉选择unix就可以了 http://www.cnblogs.com/sunfa ...

  4. CSS学习笔记三:自定义单选框,复选框,开关

    一点一点学习CCS,这次学习了如何自定义单选框,复选框以及开关. 一.单选框 1.先写好body里面的样式,先写几个框 <body> <div class="radio-1 ...

  5. 终于等到你: 图形化开源爬虫Hawk 3发布!

    超级图形化爬虫Hawk已经发布两年半时间了,2015年升级到第二版,收到上千条用户反馈(tucao),100多个红包,总共666块五毛~一直想攒着这笔钱,去北境之王天通苑的龙德商场买最心爱的阿迪王! ...

  6. Slim 文档-First Application 翻译

    最近刚好在用Slim框架,看文档的时候,中文文档中缺了这一篇没有翻译,于是我就把它翻译过来了.本人英语水平有限,如有错误请告知. 如果你在寻找创建一款非常简单的 Slim 应用程序的流程,来这里算是找 ...

  7. bugku login2 writeup 不使用vps的方法

    0x00前言 这个题是sql注入与命令执行相结合的一个题,思路有两个: 一.:sql注入登录web系统,命令执行反弹公网IP监听端口(需要vps),此种方法详见链接:http://www.bugku. ...

  8. LogicalDOC office预览中文乱码的问题

    近期在试用LogicalDOC,一个文档管理系统. 上传的office文件预览中文乱码 问题原因是LibreOffice缺少对应的中文字体导致,只需要把对应的中文字体拷贝到/opt/libreoffi ...

  9. redis主从相关问题

    redis主从是如何实现同步的 第一次.Slave向Master同步的实现是:        Slave向Master发出同步请求(发送sync命令),Master先dump出rdb文件,然后将rdb ...

  10. HTTP协议GET HEAD简单介绍

    一.HTTP协议简介 超文本传输协议(Hypertext Transfer Protocol,简称HTTP)是应用层协议,自 1990 年起,HTTP 就已经被应用于 WWW 全球信息服务系统. HT ...