本篇博文将介绍java并发底层的实现原理,我们知道java实现的并发操作最后肯定是由我们的CPU完成的,中间经历了将java源码编译成.class文件,然后进行加载,然后虚拟机执行引擎进行执行,解释为汇编语言,然后转为操作系统指令,然后转为1,0,最后CPU进行识别执行。

提到java的并发,我们不由的就会想到java中常见的键字:volatile和synchronized,我们接下来就会从这两个关机字展开分析:

  • volatile的底层实现原理
  • synchronized的实现原理和应用

volatile

说到volatile,在java的面试中面试官可是最喜欢问的问题了。看到它我们首先想到的便是保持线程间的可见性,是一个轻量级的synchronized,在一些情况下它可以代替synchronized。

volatile的作用:

一个被volatie修饰的变量,java内存模型会保证所有的线程看见的变量值是一致的。

volatile的工作原理:

我们可以定义一个volatile变量,并对他进行赋值,并通过工具来获取jit编译器生成的汇编指令,我们会发现在对volatile变量进行写操作时,会多出一条指令:以lock为前缀的指令:

lock为前缀的指令在多核处理器下回引发两件事情:

①将当前处理器缓存行的数据回写到内存中。

②这个回写内存的操作会使得在其他cpu里缓存了改内存地址的数据无效。

当我们知道了以上两点,我们就不难理解volatie变量的机制了。

在多处理器下,为了保证各个处理器的缓存是一致的,会实现缓存一致性协议,每个处理器通过嗅探在总线上的传播的数据来检查自己缓存的值是不是过期了。


synchronized

想到多线程的并发,其实我第一个想到的便是这个synchronized,翻译过来为同步,我们都知道它是一个重量级锁,当对一个方法或者代码块使用它时,当一个线程获得了这个锁,那么其它的线程就会陷入挂起状态,在java中也就表现为sleep状态,我们都知道线程的挂起和运行时要转入操作系统的内核态的(与内核态对应的便是用户态),这样特别浪费cpu资源,所以这个重量级锁是名副其实的!

但是,java SE 1.6过后java的维护团队对它进行了一系列的优化(这些优化后面一一讲述),他也就没那么“重”了,以前还有优势的可重入锁也变得没那么有优势了(ReentrantLock)。

一下我们就下列几个方面讲述synchronized:

  • 利用synchronized实现同步的基础
  • synchronized是如何实现锁的
  • 偏向锁,轻量级锁(自旋锁),重量级锁
  • 锁的升级
  • java如何实现原子操作

①利用synchronized实现同步的基础:

我们在开发中或者java的源码中都能看见synchronized的身影,例如HashTable,StringBuilder等地方,常见有两种方式:

Ⅰ丶同步方法

同步方法只需要在方法前加上synchronized便可,当一个线程执行它的时候其他线程便会陷入等待,直到它释放锁。对方法使用又可以分为两种:对普通同步方法和对静态方法,它们之间的差别是加锁的对象不同,普通方法加锁的位置是当前的对象,而静态方法加锁的位置是当前类的Class对象。

Ⅱ丶同步方法块

同步方法块加锁的是Synchronized后括号里配置的对象,这个对象可以是一个值以及任何一个变量或者对象。

②synchronized是如何实现锁的:

在jvm的规范中可以看到synchronized在jvm中的实现原理,jvm基于进入和退出Monitor对象来实现同步方法和代码块的同步,代码块是使用monitorenter和monitorexit指令来实现的,而同步方法jvm规范里没有具体给出,但是我相信具体的原理应该相差不大,无非是将java源码编译为class文件,在class字节码文件中对使用synchronized的方法进行一个标记,在字节码引擎执行这个方法的时候会对这个方法进行同步处理。

③偏向锁,轻量级锁(自旋锁),重量级锁:

在讲锁之前我们需要知道java对象头,java的对象头:

synchronized使用的锁是存储在java对象头里的,java对象头里面有32bit/64bit(视操作系统的位数而定)长度的MarkWord 里面存储了对象的hashCode和锁的信息等,在MarkWord中有2bit的空间来表示锁的状态00,01,10,11,分别表示轻量级锁,偏向锁,重量级锁,GC标记。

偏向锁:偏向锁也就人称它为偏心锁,从名字我们就可以看出来,它是一个偏向某一个线程的锁。

在实际的开发中,我们发现多线程并发,大多数执行同步方法的都是同一个线程,出现多个线程争抢一个方法的概率比较低,所以重复的获取锁和释放锁就会产生大量的资源浪费,所以为了让线程获得锁的代价更低引入了偏向锁,当一个线程访问一个同步块并获得锁时,会在对象头和线程的栈帧中的锁记录中存储偏向锁的线程ID,以后该线程进入和退出同步块时不需要进行CAS操作来进行加锁和解锁,只需要简单的查看对象头的MarkWord里是否还存有指向当前的偏向锁(在MarkWord中每个对象还有一个偏向锁标志位用来表示当前对象是否支持偏向锁,我们可以使用jvm参数来设定偏向锁)。

关于偏向锁的释放,偏向锁使用了等到存在竞争时才释放锁的机制,所以当有其他线程尝试竞争偏向锁的时候持有偏向锁的线程才会释放锁。

注意:在java6,7中偏向锁是默认启动的

轻量级锁:

轻量级锁就是在执行同步块之前,jvm会在当前线程的栈帧中创建用于存储锁记录的的空间,并将对象头中的MarkWord复制到里面,然后线程将尝试将对象头内的MarkWord替换为指向锁记录的指针,如果成功,当前线程获得锁,如果失败,表示其他线程竞争锁,当前线程便自旋来获得锁。

④锁的升级:

当前线程如果无法试用上面的方法获得锁,那么表示当前的锁存在竞争,锁就会升级为重量级锁。

轻量级锁和偏向锁的区别:

轻量级锁是在无竞争的情况下使用CAS操作去消除同步使用的互斥量,而偏向锁就是在无竞争的情况下把整个同步都去除,连CAS操作都不做!

⑤ java如何实现原子操作:

在了解java是如何实现原子操作之前,我们要知道处理器是如何实现原子操作的:

处理器一般分为两种方法执行原子操作:缓存加锁和总线加锁,其中缓存加锁比较优秀而总线加锁则比较消耗资源。(关于两种加锁的方式我们这里不做过多解释,具体在操作系统中有详细的讲解)

java使用(大多数情况下)循环CAS实现原子操作,但是使用CAS实现原子操作也会出现下面的一些经典的问题:

一)ABA问题

jdk中提供AtomicStampedReference类来解决(提供检查预期引用和预期标志)

二)循环时间长开销大

无法解决,这个是循环的通病

三)只能保证一个共享变量的原子操作

jdk中提供一个AtomicReference来解决,将多个共享变量放置在一个类中进行CAS操作。

以上为自己学习的笔记和一些自己的感想,以便博主以后查阅,也供大家参考如果错误和侵权还请联系我,改正,侵删!

参考书籍:《并发编程的艺术》

Java并发之底层实现原理学习笔记的更多相关文章

  1. 深入理解Java并发之synchronized实现原理

    深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解Java类加载器(ClassLoader) 深入 ...

  2. 20145213《Java程序设计》第八周学习笔记

    20145213<Java程序设计>第八周学习笔记 教材学习内容总结 "桃花春欲尽,谷雨夜来收"谷雨节气的到来意味着寒潮天气的基本结束,气温回升加快.刚出冬的我对于这种 ...

  3. Java AOP的底层实现原理

    Java AOP的底层实现原理 一.什么是AOP 1.AOP:Aspect Oriented Programming(面向切面编程),OOP是面向对象编程,AOP是在OOP基础之上的一种更高级的设计思 ...

  4. Java volatile 关键字底层实现原理解析

    本文转载自Java volatile 关键字底层实现原理解析 导语 在Java多线程并发编程中,volatile关键词扮演着重要角色,它是轻量级的synchronized,在多处理器开发中保证了共享变 ...

  5. Java架构师-十项全能学习笔记(1)

    Java架构师-十项全能学习笔记(1) @Configuration @EnableStateMachine public class OrderStateMachineConfig extends ...

  6. 【java并发编程艺术学习】(三)第二章 java并发机制的底层实现原理 学习记录(一) volatile

    章节介绍 这一章节主要学习java并发机制的底层实现原理.主要学习volatile.synchronized和原子操作的实现原理.Java中的大部分容器和框架都依赖于此. Java代码 ==经过编译= ...

  7. Java架构师之路 Spring学习笔记(一) Spring介绍

    前言 这是一篇原创的Spring学习笔记.主要记录我学习Spring4.0的过程.本人有四年的Java Web开发经验,最近在面试中遇到面试官总会问一些简单但我不会的Java问题,让我觉得有必要重新审 ...

  8. Unity3D 骨骼动画原理学习笔记

    最近研究了一下游戏中模型的骨骼动画的原理,做一个学习笔记,便于大家共同学习探讨. ps:最近改bug改的要死要活,博客写的吭哧吭哧的~ 首先列出学习参考的前人的文章,本文较多的参考了其中的表述: 1. ...

  9. j2ee开发之Spring2.5框架学习笔记

    Spring 2.5框架学习笔记 1.是一个开源的控制反转IOC和面向切面AOP的容器框架 2.IOC控制反转 public class PersonServiceBean { private Per ...

随机推荐

  1. hicoder1142 三分求极值

    在直角坐标系中有一条抛物线y=ax^2+bx+c和一个点P(x,y),求点P到抛物线的最短距离d. 我们代入公式,有: $d = min(\sqrt{(X - x)^2+(aX^2+bX+c-y)^2 ...

  2. float和double的区别

    1.float是单精度类型,精度有效数字为6位,超出则会四舍五入,取值范围为10的-38次方到10的38次方,float占用存储空间为4个字节. 2.double是双精度类型,精度有效数字为15位,超 ...

  3. ABAP CDS Table Function介绍与示例

    Core data services(以下简称CDS)可以指两样东西,一个是HANA CDS,一个是ABAP CDS. 如我们所知,HANA CDS只支持HANA数据库,ABAP CDS理论上支持多种 ...

  4. UITableView使用过程中可能遇到的问题

    前言:记录一些UITableView使用过程中可能遇到的问题 环境:Xcode9 解决UITableViewStyleGrouped类型的TableView的cell距离顶部有距离的问题: table ...

  5. django.db.utils.OperationalError: 1050解决方案

    manage.py migrate时进行同步数据库时出现问题;django.db.utils.OperationalError: (1050, "Table '表名' already exi ...

  6. JAVA通过Gearman实现MySQL到Redis的数据同步(异步复制)

    MySQL到Redis数据复制方案 无论MySQL还是Redis,自身都带有数据同步的机制,像比较常用的 MySQL的Master/Slave模式 ,就是由Slave端分析Master的binlog来 ...

  7. javaWeb中URLEncoder.encode空格问题

    近期开发一个在线坐席的功能.发现推送的消息中空格变成了+ .查询发现URLEncoder.encode的问题.曾经用的时候也没注意过,解决的方法网上是对URLEncoder.encode的之后的字符串 ...

  8. 十条很实用的jQuery代码片段

    本文转自:http://developer.51cto.com/art/201604/509093.htm 作者:核子可乐译来源:51CTO 原文标题:10 jQuery Snippets for E ...

  9. 动态规划-迷宫-百度之星-Labyrinth

    Labyrinth Problem Description 度度熊是一仅仅喜欢探险的熊.一次偶然落进了一个m*n矩阵的迷宫,该迷宫仅仅能从矩阵左上角第一个方格開始走,仅仅有走到右上角的第一个格子才算走 ...

  10. c++中虚多态的实现机制

    c++中虚多态的实现机制 參考博客:http://blog.csdn.net/neiloid/article/details/6934135 序言 证明vptr指针存在 无继承 单继承无覆盖 单继承有 ...