1.适应性自旋锁

首先什么是自旋锁?有时候线程去竞争锁失败,进入了阻塞状态,但刚刚进入阻塞状态后持有锁的线程就释放了锁资源,这个时候线程又会被唤醒继续执行,考虑到这种情况,jdk1.4中引入了自旋锁的概念,就是在sychronized同步代码块中,如果线程没有竞争到锁,则让它先进行一段无意义的自旋,避免线程直接进入阻塞状态,自旋结束后如果持有锁的线程已释放锁,则当前线程可直接开始执行。显然,这样处理会有个坏处,如果每一次线程未竞争到锁都进行一段时间的自旋,且每一次自旋结束后都依然没有获取到锁,那么自旋的操作就白白浪费了cpu的性能。在jdk1.4引入自旋锁的时候我们可以通过jvm启动参数-XX:+UseSpinning开启自旋锁,使用-XX:PreBlockSpin设置自旋锁的等待次数,而jdk1.6引入适应性自旋锁后,自旋锁机制变得更加"聪明",它可以动态的调整每一次自旋的时间,调整的依据就是上一个获取到锁的线程获取锁用了多少时间,如果用时较短,它就认为这个锁比较容易获取,那就适当的延长自旋的时间以等待这个锁的释放,如果用时较长,则缩短当前线程自旋的时间或者不自旋直接进入阻塞状态,以避免长时间自旋后依然无法获取锁的情况。

2.锁消除

锁消除简单来说,就是在不可能出现锁资源的竞争,不需要加锁的情况下,如果我们使用了synchronized锁,则jvm会在编译的时候直接将这个锁消除,以减少锁的操作,提升代码的执行效率。jvm判断这个方法是否线程安全,是否不可能出现锁资源竞争的时候,会进行逃逸分析,所以锁消除有两个前提,1.该程序必须以server模式运行 2.必须开启逃逸分析和锁消除(通过jvm启动参数设置,-XX:+DoEscapeAnalysis表示开启逃逸分析,-XX:+EliminateLocks表示锁消除)

3.锁粗化

一般在加锁的时候,都会让锁的粒度足够小,尽量在synchronized同步代码块中只包含真正需要加锁的代码,以减少锁占用的时间,提高代码执行性能。但有时候,一味的减小锁的粒度,会增加竞争锁的次数,同一个线程会频繁获取同一个锁,出现这种情况时,sychronized会自动帮我们提升锁的粒度,减少竞争锁的次数,如下,以极端情况举例:

锁粗化前:

public void test(){for (int i=0;i<10;i++){synchronized (this){count++;}}
}

锁粗化后:

public void test() {synchronized (this) {for (int i = 0; i < 10; i++) {count++;}}
}

4.偏向锁和轻量级锁

锁的分类

jdk1.6之后,synchronized有偏向锁、轻量级锁、重量级锁三种锁机制。synchronized有一个特点,就是使用synchronized同步代码块或synchronized方法时,所有的对象都可以作为锁对象,用synchronized修饰的方法,锁对象为当前类的实例对象;synchronized修饰的静态方法,锁对象为当前类的class对象。那既然所有对象都能作为synchronized的锁,那储存对象的锁信息(当前对象的锁状态,被哪个线程持有锁,有哪些线程在等待锁等)最好的方式就是将其存储在对象的对象头中。

对象头

在HotSpot虚拟机中,对象在内存中的分布可分为三个部分:1.对象头 2.实例信息 3.数据填充。其中对象头又分为Mark Word和类型指针两部分,Mark Word用于存储对象自身的运行时数据,如:GC分代年龄、hashcode、数组长度(如果该对象为数组)、锁状态标志、是否偏向、偏向线程id等等。类型指针部分则存储该对象指向其类的元数据的指针。

Mark Word的存储特点

在32位和64位的系统中,对象头的Mark Word分别占用32个和64个bitmap,为了尽可能多的存储对象运行时的数据,Mark Word被设计成了可根据锁状态动态分配各部分存储空间大小的数据结构,见下图

几种锁的状态 

    1.无锁状态

当对象为无锁状态时,Mark Word主要存储对象的hashcode、分代年龄等信息,如果对象为数组类型,则还会存储该数组的长度。这就是为什么通过类的元数据可以直接获取对象的大小,但通过类的元数据不能获取数组的长度。

2.偏向锁

当线程进入synchronized同步代码块或同步方法时,如果对象锁的对象头中保存的偏向锁线程id为0,则该对象会在Mark Word中以CAS的方式保存当前线程的线程id。如果有另一个线程id来竞争这个对象锁,则升级为轻量级锁

适用场景:很多函数(特别是第三方包提供的)虽然在设计时考虑了并发的情况,做了线程安全的控制,但在实际使用场景中,或许根本就只有一条线程在调用,这种情况就可以使用偏向锁,将使用这个锁的线程id记录,避免同一条线程频繁的竞争和释放同一个锁而带来性能开销。而一旦出现另一条线程来获取锁,说明这个锁对象是存在锁资源竞争的,则将该对象的锁状态升级为轻量锁。

3.轻量级锁

当对象的锁状态为轻量级锁时,MarkWord区记录的是“指向栈中锁记录的指针”,这个指针指向的是当前持有锁的线程的栈空间中的一个栈帧,这个栈帧叫做lock record,如下图所示,lock record分为两部分,一部分复制保存锁对象的mark word,一部分保存锁对象的地址。

当线程第一次进入同步代码块时,会在当前线程的栈空间中生成一个lock record,将锁对象的mark word区保存的对象头信息保存下来,并且记录锁对象的地址,然后通过CAS的方式将lock record的地址记录到锁对象的markword,解锁时也是通过CAS的方式将lock record中记录的锁对象的hashcode、分代年龄等信息记录到锁对象的markword区,并将锁标志改为无锁状态。无论加锁还是解锁的CAS操作如果操作失败,则将锁升级为重量级锁。

适用场景:很多情况下,我们会尽量让锁的粒度足够小,以较少锁占用的时间,所以在并发量不大时,同步块中的代码其实是各个线程交替执行而并不会发生锁竞争的,这种时候就应该使用轻量级锁,加锁和解锁都使用CAS操作,不会涉及线程上下文的切换、用户态和内核态的切换。而出现锁竞争的情况时,就升级为重量级锁。

总结:

偏向锁适用于只有一条线程访问同步块,轻量级锁适用于多条线程交替访问同步块,重量级锁适用于多条线程同时访问同步块。

Synchronized在JDK1.6中的优化相关推荐

  1. synchronized在JDK6做了哪些优化

    记得在大学中那个时候刚开始学习java, 需要遇到多线程需要加锁的操作时,不管不顾全部都用synchronized,相对于当时的我们来说synchronized是这么的神奇而又强大,那个时候我们赋予它 ...

  2. Java 多线程(三)线程间的通信jdk1.5中Lock,Condition---生产者消费者为例

    1.使用Lock和Condition的意义 JDK1.5中提供了多线程升级的解决方案. 1.将同步synchronized替换成现实Lock操作. 2.将Object中的wait,notify,not ...

  3. 详述 JDK1.7 中 HashMap 会发生死链的原因

    文章目录 前置知识 死循环执行步骤1 死循环执行步骤2 死循环执行步骤3 解决方案 总结 前置知识 HashMap死循环是一个比较常见.比较经典的问题,在日常的面试中出现的频率比较高,所以接下来咱们通 ...

  4. JDK1.8中的HashMap

    目录 JDK1.8中的HashMap 问题1:为什么要将1.7中HashMap的链表结构改为红黑树? 问题2:为什么HashMap要用红黑树?红黑树好在哪里. 二叉查找树概述 红黑树概述 红黑树的插入 ...

  5. 七、JDK1.7中HashMap扩容机制

    导读 前面文章一.深入理解-Java集合初篇 中我们对Java的集合体系进行一个简单的分析介绍,上两篇文章二.Jdk1.7和1.8中HashMap数据结构及源码分析 .三.JDK1.7和1.8Hash ...

  6. JDK1.7中HashMap底层实现原理

    JDK1.7中HashMap底层实现原理 一.数据结构 HashMap中的数据结构是数组+单链表的组合,以键值对(key-value)的形式存储元素的,通过put()和get()方法储存和获取对象. ...

  7. 八、JDK1.8中HashMap扩容机制

    导读 前面文章一.深入理解-Java集合初篇 中我们对Java的集合体系进行一个简单的分析介绍,上两篇文章二.Jdk1.7和1.8中HashMap数据结构及源码分析 .三.JDK1.7和1.8Hash ...

  8. 深度学习中的优化简介

    深度学习算法在许多情况下都涉及到优化. 1. 学习和纯优化有什么不同 在大多数机器学习问题中,我们关注某些性能度量P,其定义于测试集上并且可能是不可解的.因此,我们只是间接地优化P.我们系统通过降低代 ...

  9. 深度学习中的优化算法串讲

    Datawhale原创 作者:谢文睿,Datawhale成员 寄语:优化算法是一个超参数,一个优化算法不是适合所有损失函数的,没有哪个优化算法是绝对的好或绝对的坏,是要根据损失函数判断的 本文对深度学 ...

最新文章

  1. 1、java集合:java集合详解及类关系图
  2. Java-InnerClass内部类
  3. leetcode之Tow Sum两数之和的三种思路
  4. android cpu绑核
  5. windows 安装yaml支持和pytest支持等
  6. [luogu-P4299] 首都(并查集 + LCT动态维护树的重心 / 维护虚儿子信息)
  7. iZotope RX 9 Advanced for Mac - 高级音频修复软件
  8. 开发者的盛宴Google I/O 2016
  9. html 在线图片压缩,JS HTML图片显示Canvas 压缩功能
  10. SQL Server字符串处理函数大全
  11. revit二次开发概念_Revit二次开发那些事儿
  12. 银河麒麟桌面操作系统V10node.js 14安装小助手
  13. 新浪sae php,PHP+新浪微博开放平台+新浪云平台(SAE)1
  14. 格兰杰检验的基本步骤_实证检验步骤
  15. 2017中国十大富豪排行榜
  16. WEB安全(十六)单点登录的基本实现
  17. 共阴数码管显示0~9的数字
  18. Mysql(三)Mysql索引基本原理
  19. 前端面试题汇总(含答案)(HTML+CSS篇)
  20. 3dmax骨骼的绑定

热门文章

  1. 【多轮对话】任务型多轮对话状态跟踪-NBT原理
  2. 【C++】打怪小游戏
  3. java etl工具_一文带你入门ETL工具-datax的简单使用
  4. 女人变美丽的十大秘诀
  5. 解决无耻迅雷在后台偷偷上传文件的一种方法
  6. git revert讲解
  7. 【只推荐一位Python大佬】 从程序员到创业者,再到自由职业
  8. 数据库中三种范式的讲解
  9. CFI --- Common Flash Interface
  10. CFI技术新探索,struct_san今日登场