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操作。

(0)

相关推荐

  • java并发学习之BlockingQueue实现生产者消费者详解

    1.介绍 阻塞队列 (BlockingQueue)是Java util.concurrent包下重要的数据结构,BlockingQueue提供了线程安全的队列访问方式:当阻塞队列进行插入数据时,如果队列已满,线程将会阻塞等待直到队列非满:从阻塞队列取数据时,如果队列已空,线程将会阻塞等待直到队列非空.并发包下很多高级同步类的实现都是基于BlockingQueue实现的. JDK7提供了以下7个阻塞队列: ArrayBlockingQueue :由数组结构组成的有界阻塞队列. LinkedBloc

  • 聊聊Java并发中的Synchronized

    1 引言 在多线程并发编程中Synchronized一直是元老级角色,很多人都会称呼它为重量级锁,但是随着Java SE1.6对Synchronized进行了各种优化之后,有些情况下它并不那么重了,本文详细介绍了Java SE1.6中为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻量级锁,以及锁的存储结构和升级过程. 2 术语定义 术语 英文 说明 CAS Compare and Swap 比较并设置.用于在硬件层面上提供原子性操作.在 Intel 处理器中,比较并交换通过指令cmpxch

  • Java并发问题之乐观锁与悲观锁

    首先介绍一些乐观锁与悲观锁: 悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁.传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁.再比如Java里面的同步原语synchronized关键字的实现也是悲观锁. 乐观锁:顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版

  • java并发问题概述

    1什么是并发问题. 多个进程或线程同时(或着说在同一段时间内)访问同一资源会产生并发问题. 银行两操作员同时操作同一账户就是典型的例子.比如A.B操作员同时读取一余额为1000元的账户,A操作员为该账户增加100元,B操作员同时为该账户减去50元,A先提交,B后提交.最后实际账户余额为1000-50=950元,但本该为1000+100-50=1050.这就是典型的并发问题.如何解决?可以用锁. 2java中synchronized的用法 用法1 public class Test{ public

  • java并发编程之同步器代码示例

    同步器是一些使线程能够等待另一个线程的对象,允许它们协调动作.最常用的同步器是CountDownLatch和Semaphore,不常用的是Barrier和Exchanger 队列同步器AbstractQueuedSynchronizer是用来构建锁或者其他同步组件的基础框架,它内部使用了一个volatiole修饰的int类型的成员变量state来表示同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作. 同步器的主要使用方式是继承,子类通过继承同步器并实现它的抽象方法来管理同步状态,在抽

  • 深入分析java并发编程中volatile的实现原理

    引言 在多线程并发编程中synchronized和Volatile都扮演着重要的角色,Volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的"可见性".可见性的意思是当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值.它在某些情况下比synchronized的开销更小,本文将深入分析在硬件层面上Inter处理器是如何实现Volatile的,通过深入分析能帮助我们正确的使用Volatile变量. 术语定义 术语 英文单词 描述 共享变量 在多个线

  • Java并发实例之CyclicBarrier的使用

    最近一直整并发这块东西,顺便写点Java并发的例子,给大家做个分享,也强化下自己记忆,如果有什么错误或者不当的地方,欢迎大家斧正. CyclicBarrier是一种多线程并发控制实用工具,和CountDownLatch非常类似,它也可以实现线程间的计数等待,但是它的功能比CountDownLatch更加复杂且强大. CyclicBarrier的介绍 CyclicBarrier 的字面意思是可循环(Cyclic)使用的屏障(Barrier).它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)

  • java并发等待条件的实现原理详解

    前言 前面介绍了排它锁,共享锁的实现机制,本篇继续学习AQS中的另外一个内容-Condition.想必学过java的都知道Object.wait和Object.notify,同时也应该知晓这两个方法的使用离不开synchronized关键字.synchronized是jvm级别提供的同步原语,它的实现机制隐藏在jvm实现中.作为Lock系列功能中的Condition,就是用来实现类似 Object.wait和Object.notify 对应功能的. 使用场景 为了更好的理解Lock和Condit

  • Java并发底层实现原理学习心得

    我们知道java实现的并发操作最后肯定是由我们的CPU完成的,中间经历了将java源码编译成.class文件,然后进行加载,然后虚拟机执行引擎进行执行,解释为汇编语言,然后转为操作系统指令,然后转为1,0,最后CPU进行识别执行. 提到java的并发,我们不由的就会想到java中常见的键字:volatile和synchronized,我们接下来就会从这两个关机字展开分析: volatile的底层实现原理 synchronized的实现原理和应用 volatile 说到volatile,在java

  • Java CAS底层实现原理实例详解

    这篇文章主要介绍了Java CAS底层实现原理实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一.CAS(compareAndSwap)的概念 CAS,全称Compare And Swap(比较与交换),解决多线程并行情况下使用锁造成性能损耗的一种机制. CAS(V, A, B),V为内存地址.A为预期原值,B为新值.如果内存地址的值与预期原值相匹配,那么将该位置值更新为新值.否则,说明已经被其他线程更新,处理器不做任何操作:无论哪种情

  • Java并发CopyOnWrite容器原理解析

    这篇文章主要介绍了Java并发CopyOnWrite容器原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 Copy-On-Write简称COW,是一种用于程序设计中的优化策略.其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容Copy出去形成一个新的内容然后再改,这是一种延时懒惰策略.从JDK1.5开始Java并发包里提供了两个使用CopyOnWrite机制实现的并发容器,它们是CopyOnWri

  • Java synchronized底层实现原理以及锁优化

    目录 一.概述 synchronized简介 synchronized作用 synchronized的使用 二.实现原理 三.理解Java对象头 四.JVM对synchronized的锁优化 1.偏向锁 2.轻量级锁 3.重量级锁 4.自旋锁 5.锁消除 6.锁粗化 总结 一.概述 synchronized简介 在多线程并发编程中 synchronized 一直是元老级角色,很多人都会称呼它为重量级锁.但是,随着 Java SE 1.6 对synchronized 进行了各种优化之后,有些情况下

  • java反射机制的一些学习心得小结

    概述 之前在了解Spring的类加载机制的时候,了解了java的反射机制.但是,我对反射理解一直不深.也一直有点疑惑:Spring为什么利用反射创建对象?直接new对象和依靠反射创建对象有什么区别?什么是动态加载类? 什么是反射? 要想知道反射到底是什么,首先需要知道java的类加载和对象创建的机制. 当我们写完一个java文件的时候,后缀是.java.在我们利用IDE执行java文件的时候,其实IDE也帮我们运行了javac,即java编译器.编译器会将.java文件编译成.class文件.j

  • Java LinkedHashMap 底层实现原理分析

    在实现上,LinkedHashMap很多方法直接继承自HashMap,仅为维护双向链表覆写了部分方法.所以,要看懂 LinkedHashMap 的源码,需要先看懂 HashMap 的源码. 默认情况下,LinkedHashMap的迭代顺序是按照插入节点的顺序.也可以通过改变accessOrder参数的值,使得其遍历顺序按照访问顺序输出. 这里我们只讨论LinkedHashMap和HashMap的不同之处,LinkedHashMap的其他操作和特性具体请参考HashMap 我们先来看下两者的区别:

  • Java并发的CAS原理与ABA问题的讲解

    CAS原理 在计算机科学中,比较和交换(Compare And Swap)是用于实现多线程同步的原子指令. 它将内存位置的内容与给定值进行比较,只有在相同的情况下,将该内存位置的内容修改为新的给定值. 这是作为单个原子操作完成的. 原子性保证新值基于最新信息计算; 如果该值在同一时间被另一个线程更新,则写入将失败. 操作结果必须说明是否进行替换; 这可以通过一个简单的布尔响应(这个变体通常称为比较和设置),或通过返回从内存位置读取的值来完成(摘自维基本科) CAS流程 以AtomicIntege

  • Java synchronize底层实现原理及优化

    首先来说下synchronize和Lock的区别: 两者都是锁,用来控制并发冲突,区别在于Lock是个接口,提供的功能更加丰富,除了这个外,他们还有如下区别: synchronize自动释放锁,而Lock必须手动释放,并且代码中出现异常会导致unlock代码不执行,所以Lock一般在Finally中释放,而synchronize释放锁是由JVM自动执行的. Lock有共享锁的概念,所以可以设置读写锁提高效率,synchronize不能.(两者都可重入) Lock可以让线程在获取锁的过程中响应中断

  • Java同步关键字synchronize底层实现原理解析

    目录 1 字节码层实现 1.1 InterpreterRuntime::monitorenter 1.1.1 函数参数 JavaThread *thread 1.1.2 函数体 2 偏向锁 2.1 偏向锁的意义 2.2 偏向锁的获取 2.2.1 markOop mark = obj->mark() 2.2.2 判断mark是否为可偏向状态 2.2.3 判断mark中JavaThread的状态 2.2.4 通过CAS原子指令 2.2.5 如果执行CAS失败 2.3 偏向锁的撤销 2.4 轻量级锁

  • Java并发编程深入理解之Synchronized的使用及底层原理详解 下

    目录 一.synchronized锁优化 1.自旋锁与自适应自旋 2.锁消除 逃逸分析: 3.锁粗化 二.对象头内存布局 三.synchronized锁的膨胀升级过程 1.偏向锁 2.轻量级锁 3.重量级锁 4.各种锁的优缺点 接着上文<Java并发编程深入理解之Synchronized的使用及底层原理详解 上>继续介绍synchronized 一.synchronized锁优化 高效并发是从JDK 5升级到JDK 6后一项重要的改进项,HotSpot虚拟机开发团队在这个版本上花费了大量的资源

随机推荐