热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

synchronize和CAS

程序有两种不同的级别:用户态、内核态 原来的操作系统中没有这种级别之分,因此一个程序可能访问其他程序的内存,就可能把整个机器都干掉。一般来说,操作系统跑在内核态,内核态的程序可以访问硬件(如内存,

程序有两种不同的级别:用户态、内核态
原来的操作系统中没有这种级别之分,因此一个程序可能访问其他程序的内存,就可能把整个机器都干掉。一般来说,操作系统跑在内核态,内核态的程序可以访问硬件(如内存,进行线程调度等),当用户态的程序需要访问硬件时,就需要向操作系统申请。在CPU硬件上分为ring0 – 1 – 2 -3,应用程序一般跑在ring3级,操作系统跑在ring0级,ring0级的程序可以直接访问硬件而ring3级不行。

有轻量级锁和重量级锁之分,轻量级锁都是在用户态完成的,而重量级锁需要进过操作系统。

1. CAS

CAS的全称为CompareAndSwap,它是一条CPU并发原语
功能:比较当前工作内存中的值和主内存中的值,如果相同则执行规定操作。如果不相同则继续比较直到工作内存中的值和主内存中的值相等为止。

CAS并发原语体现在JAVA语言中就是sun.misc.Unsafe类中的各个方法。调用Unsafe类中的CAS方法,JVM会帮我们实现出CAS的汇编指令(lock cmpxchg指令)。这是一种完全依赖于硬件的功能(锁总线),通过它实现了原子操作。原语的执行必须是连续的,在执行的过程中不允许被中断,也就是说CAS是一条CPU的原子指令,线程安全。

2.Unsafe类

是CAS的核心类,存在于sun.misc包中,其内部方法操作可以像C的指针一样直接操作内存,所有方法都被native修饰。变量valueoffset,表示该变量值在内存中的偏移地址,Unsafe就根据内存的偏移地址获取数据,value被volatile修饰,保证了多线程之间的内存可见性。

synchronize和CAS
CAS.png

不断的循环看当前内存地址中的值是否是我期待的值,如果是则进行下面的操作,如果不是则继续循环。

缺点:1. 循环时间长开销大(如果CAS失败,会一直进行尝试)
​ 2. 只能保证一个共享变量的原子操作
3. ABA问题

2. synchronized的锁升级过程

synchronize和CAS
Hotspot的锁实现.png

Java SE 1.6为了减少获得锁和释放锁带来的性能消耗,引入了偏向锁和轻量级锁。
在1.6中,锁一共有4中状态

  • 无锁状态
  • 偏向锁状态
  • 轻量级锁状态
  • 重量级锁状态
  1. 偏向锁意义:据调查,大多数情况下,锁总是被同一线程调用,所以就出现了偏向锁。
    偏向锁总是偏向于第一次获取锁的线程,一般无竞争状态时使用的就是偏向锁,可以使用-XX:+/-UseBiasedLocking参数启用/关闭偏向锁,默认是开启的(线程数多的时候,适合关闭偏向锁,因为竞争激烈,锁会升级,关闭后可以省略升级过程,直接使用轻量级锁)。

偏向锁加锁过程:
1)执行到monitorenter(使用monitorenter和monitorexit两个指令实现同步)时,首先检查锁标志是否为01(01表示未上锁)
2)其次检查偏向锁位是否为0(0表示无锁,1表示当前已是偏向锁)
3)如果检查成功,通过CAS操作,将Mark Word中的线程ID设置为自己的线程ID,然后将偏向锁位设置为1
4)如果第二次仍是该线程进入同步区,那么不再需要执行第三步
5)如果第二次是其他线程进入,那么锁升级
竞争激烈,多个线程同时争锁,锁升级为轻量级锁
第一个线程进入后设置为偏向锁,第二次再来的线程不是该线程而是其他线程,锁升级为轻量级锁
 偏向锁是默认启用的,它会在程序启动几秒后才激活,可以通过JVM参数:-XX:BiasedLockingStartupDelay=0关闭延迟。也可以使用参数:-XX:UserBiasedLocking=false关闭偏向锁

  • 2.轻量级锁
    加锁过程:
    1)当某个线程想要占用这把锁的时候,它会首先在自己的栈(帧)中创建一个锁记录(LockRecord)
    2)然后将Mark Word信息复制(hashCode、分代年龄等)到LockRecord中
    3)用CAS操作将Mark Word替换掉LR指针,即将Mark Word中除了标志位外的部分替换成一个指针
    4)指向自己的LockRecord哪个线程替换成功,哪个线程就成功得到了锁,轻量级锁自旋次数超过10(任意一个线程),或者正在自旋的线程超过CPU核数一半,那么轻量级锁升级为重量级锁(jdk1.6以前,jdk1.6以后默认启动自适应自旋),可以通过-XX:+PreBlockSpin指定自旋次数。

3.重量级锁
因为需要阻塞,并且需要向操作系统级别申请,所以说是重量级。重量级锁是操作系统来决定由哪个线程来获得锁的:(用户态到内核态的访问
1)生成或者复用monitor对象
2)Mark Word不再指向LR,而是指向monitor对象
3)线程阻塞,进入_EntryList排队,由操作系统决定唤醒谁
4)操作系统唤醒线程后该线程的Mark Word替换为monitor pointer,然后执行

monitor对象
 monitor可以理解为一个操作系统级别的互斥变量,当一个线程想要执行一段被synchronized圈起来的同步方法或者代码块时,该线程得先获取到synchronized修饰的对象对应的monitor。
在hotSpot虚拟机中,monitor是由ObjectMonitor实现的。其源码是用c++来实现的

Java代码里不会显示地去创造这么一个monitor对象,我们也无需创建,事实上可以这么理解:我们是通过synchronized修饰符告诉JVM需要为我们的某个对象创建关联的monitor对象

synchronize和CAS
锁升级.png

整个过程:new对象的时候首先看是否开启了偏向锁,启动了就匿名偏向(即没有偏向任何线程),匿名偏向直接升级为偏向锁,偏向锁轻度竞争升级为轻量级锁,再重度竞争则升级成重量级锁。没有启动偏向锁就生成普通对象,普通对象一般直接升级为轻量级锁,轻量级锁重度竞争升级成重量级锁。

3.打开偏向锁效率是否一定提高?为什么?

在明确知道有线程竞争的情况下,因为偏向锁是存在竞争的,启动偏向锁不是一个合适的选择。默认4s后启动偏向锁是因为JVM在启动的时候一定会产生多线程的竞争。因此默认情况下延迟4s启动偏向锁。

4. 如何解决ABA问题

使用AtomicStampedReference,带有时间戳(版本号)。


推荐阅读
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • ejava,刘聪dejava
    本文目录一览:1、什么是Java?2、java ... [详细]
  • 深入理解线程、进程、多线程、线程池
    本文以QT的方式来走进线程池的应用、线程、进程、线程池、线程锁、互斥量、信号量、线程同步等的详解,一文让你小白变大神!为什么要使用多线程、线程锁、互斥量、信号量?为什么需要线程 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 预备知识可参考我整理的博客Windows编程之线程:https:www.cnblogs.comZhuSenlinp16662075.htmlWindows编程之线程同步:https ... [详细]
  • 嵌入式处理器的架构与内核发展历程
    本文主要介绍了嵌入式处理器的架构与内核发展历程,包括不同架构的指令集的变化,以及内核的流水线和结构。通过对ARM架构的分析,可以更好地理解嵌入式处理器的架构与内核的关系。 ... [详细]
  • 如何用JNI技术调用Java接口以及提高Java性能的详解
    本文介绍了如何使用JNI技术调用Java接口,并详细解析了如何通过JNI技术提高Java的性能。同时还讨论了JNI调用Java的private方法、Java开发中使用JNI技术的情况以及使用Java的JNI技术调用C++时的运行效率问题。文章还介绍了JNIEnv类型的使用方法,包括创建Java对象、调用Java对象的方法、获取Java对象的属性等操作。 ... [详细]
  • Java和JavaScript是什么关系?java跟javaScript都是编程语言,只是java跟javaScript没有什么太大关系,一个是脚本语言(前端语言),一个是面向对象 ... [详细]
  • 篇首语:本文由编程笔记#小编为大家整理,主要介绍了软件测试知识点之数据库压力测试方法小结相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 本文介绍了Java虚拟机中的垃圾收集器,包括年轻代收集器Serial收集器、ParNew收集器、Parallel Scavenge收集器,以及老年代收集器Serial Old收集器、Parallel Old收集器和CMS收集器。对每种收集器的算法和特点进行了详细解析,希望对读者有参考价值。 ... [详细]
  • 本文概述了JNI的原理以及常用方法。JNI提供了一种Java字节码调用C/C++的解决方案,但引用类型不能直接在Native层使用,需要进行类型转化。多维数组(包括二维数组)都是引用类型,需要使用jobjectArray类型来存取其值。此外,由于Java支持函数重载,根据函数名无法找到对应的JNI函数,因此介绍了JNI函数签名信息的解决方案。 ... [详细]
author-avatar
手机用户2502895461
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有