JVM——锁
对象头[每个对象都具有对象头] Mark:对象头的标记(32位),描述对象的 hash、锁信息、垃圾回收标记、年龄;内容包括:①、指向锁记录的指针;②、指向 monitor 的指针;③、GC 标记;④、偏向锁线程 ID;
一、偏向锁
偏向锁无法使用自旋锁优化,因为一旦有其他线程申请锁,就破坏了偏向锁的假定。偏向锁的目标是,减少无竞争且只有一个线程使用锁的情况下,使用轻量级锁产生的性能消耗。轻量级锁每次申请、释放锁都至少需要一次CAS,但偏向锁只有初始化时需要一次CAS。如果明显存在其他线程申请锁,那么偏向锁将很快膨胀为轻量级锁。其特点如下:
【1】大部分情况下没有竞争的,所以可以通过偏向锁来提高性能;
【2】所谓偏向锁,就是偏心,即锁会偏向于当前已经占有的线程;
【3】将对象 Mark 的标记设置为偏向,并将线程 ID 写入对象的 Mark 头中;
【4】只要没有竞争,获得偏向锁的线程,在将来进入代码块,不需要做同步;
【5】-XX:+UseBiasedLocking -默认启动;
【6】在竞争激烈的场合,偏向锁会增加系统负担;
【代码示列】:当没有锁竞争的时候,就会默认使用偏向锁。
1 public static List<Integer> numberList =new Vector<Integer>();
2 public static void main(String[] args) throws InterruptedException {
3 long begin=System.currentTimeMillis();
4 int count=0;
5 int startnum=0;
6 while(count<10000000){
7 numberList.add(startnum);
8 startnum+=2;
9 count++;
10 }
11 long end=System.currentTimeMillis();
12 System.out.println(end-begin);
13 }
开启偏向锁:-XX:+UseBiasedLocking -XX:+BiasedLockingStartupDelay=0
关闭偏向锁:-XX:+UseBiasedLocking -XX:-BiasedLockingStartupDelay=0
【结论】:使用偏向锁,可以获得5%的性能提升。
二、轻量级锁
如果完全没有实际的锁竞争,那么申请重量级锁都是浪费的。轻量级锁的目标是,减少无实际竞争情况下,使用重量级锁产生的性能消耗,包括系统调用引起的内核态与用户态切换、线程阻塞造成的线程切换等。顾名思义,轻量级锁是相对于重量级锁而言的。使用轻量级锁时,不需要申请互斥量,仅仅将Mark Word 中的部分字节CAS更新指向线程栈中的Lock Record,如果更新成功,则轻量级锁获取成功,记录锁状态为轻量级锁;否则,说明已经有线程获得了轻量级锁,目前发生了锁竞争(不适合继续使用轻量级锁),接下来膨胀为重量级锁。当然,由于轻量级锁天然瞄准不存在锁竞争的场景,如果存在锁竞争但不激烈,仍然可以用自旋锁优化,自旋失败后再膨胀为重量级锁。BasicObjectLock:嵌入在线程中
【1】普通锁处理性能不够理想,轻量级锁是一种快速的锁定方法。
【2】如果对象没有锁定:①、将对象头的 Mark 指针保存到锁对象中。②、将对象头设置为指向锁的指针(在线程空间中)
1 lock->set_displaced_header(mark);
2 if (mark == (markOop) Atomic::cmpxchg_ptr(lock, obj()->mark_addr(), mark)) {
3 TEVENT (slow_enter: release stacklock) ;
4 return ;
5 }
6 //lock位于线程中
【3】总结:① 如果轻量级锁失败,表示存在竞争,升级为重量级锁(常规锁);② 在没有锁竞争的情况下,减少传统锁使用OS 互斥产生的锁性能消耗问题;③ 在竞争激烈时,轻量级锁会做很多额外操作,导致性能下降;
三、自旋锁
自适应自旋解决的是“锁竞争时间不确定”的问题。JVM很难感知到确切的锁竞争时间,而交给用户分析就违反了JVM的设计初衷。自适应自旋假定不同线程持有同一个锁对象的时间基本相当,竞争程度趋于稳定,因此,可以根据上一次自旋的时间与结果调整下一次自旋的时间。然而,自适应自旋也没能彻底解决该问题,如果默认的自旋次数设置不合理(过高或过低),那么自适应的过程将很难收敛到合适的值:
【1】JDK1.7 中,自旋锁为内置实现。
【2】在线程竞争存在时,如果线程可以很快获得锁,那么可以不再 OS 层挂起线程,让线程做几个空操作(自旋)
【3】如果同步块很长,自旋失败,会降低系统性能。
【4】如果同步块很短,自旋成功,节省线程挂起切换时间,提升系统性能。
四、偏向锁、轻量级锁、自旋锁总结
【1】不是 Java 语言层面的锁优化方法;
【2】内置于 JVM 中的获取锁的优化方法和获取锁的步骤:① 偏向锁可用时,会尝试偏向锁;② 轻量级锁可用会尝试轻量级锁;③ 以上都失败,尝试自旋锁;④ 在失败尝试普通锁,使用 OS互斥量在操作系统层面挂起;
五、如何合理使用锁机制
【1】减少锁持有时间(尽量使用 synchronized 块)
1 public synchronized void syncMethod(){
2 othercode1();
3 mutextMethod();
4 othercode2();
5 }
6 public void syncMethod2(){
7 othercode1();
8 synchronized(this){
9 mutextMethod();
10 }
11 othercode2();
12 }
【2】减少锁粒度:① 将大对象拆成小对象,大大增加并行度,降低锁竞争;② 偏向锁,轻量级锁成功率高;③ ConcurrentHashMap 中 HashMap 的同步实现;④ Collections.synchronizedMap(map<key,value,m>;⑤ 返回SynchronizedMap 对象。
1 public V get(Object key) {
2 synchronized (mutex) {return m.get(key);}
3 }
4 public V put(K key, V value) {
5 synchronized (mutex) {return m.put(key, value);}
6 }
ConcurrentHashMap JDK1.7时使用若干个 Segment:Segment<key,vlaue>[] segments;Segment(分段锁) 中维护 HashEntry<k,v>;Put 操作时,先定位到 Segment,锁定一个Segment,执行put;在减少粒度后,ConcurrentHashMap 允许若干个线程同时进入。
【3】锁分离:① 根据功能进行锁分离;② ReadWriteLock;③ 读多写少的情况下,可以提高性能;
④ 读写分离思想可以延伸,只要操作互不影响,锁就可以分离;⑤ LinkedBlockingQueue 链表阻塞队列
【4】锁粗化:通常情况下,为保证多线程间的有效并发,会要求每个线程持有锁的时间尽量短,即在使用完公共资源后,应该立即释放锁。只有这样,等待在这个锁上的其他线程才能今早的获得资源执行任务。但是,凡是有一个临近值,如果对同一个锁不停的释放获取,其本身也会消耗宝贵的资源,反而不利于性能的优化。
【5】锁消除:在编译时,如果发现不可能被共享的对象,则可以消除这些对象的锁操作
1 public static void main(String args[]) throws InterruptedException {
2 long start = System.currentTimeMillis();
3 for (int i = 0; i < CIRCLE; i++) {
4 craeteStringBuffer("JVM", "Diagnosis");
5 }
6 long bufferCost = System.currentTimeMillis() - start;
7 System.out.println("craeteStringBuffer: " + bufferCost + " ms");
8 }
9
10 public static String craeteStringBuffer(String s1, String s2) {
11 StringBuffer sb = new StringBuffer();
12 sb.append(s1);
13 sb.append(s2);
14 return sb.toString();
15 }
【开启锁消除】:-server -XX:+DoEscapeAnalysis -XX:+EliminateLocks ——执行时间198ms;
【关闭锁消除】:-server -XX:+DoEscapeAnalysis -XX:-EliminateLocks ——执行时间254ms;
六、无锁
① 锁是悲观的操作;② 无锁是乐观的操作;③ 无锁的实现方式:CAS(Compare and Swap)、非阻塞同步;
CAS(V,E,N)V:表示更新的变量,E:表示预期值,N:表示新值。当V=E时,才会将V的值设为N,如果 V值和 E值不同,则说明有其他线程做了更新,则当前线程什么都不做,最后 CAS 返回 V 的真实值。
在应用层面判断多线程的干扰,如果有干扰则通知线程重试。例如:java.util.concurrent.atomic.AtomicInteger
1 public final int getAndSet(int newValue) {
2 for (;;) {
3 int current = get();
4 if (compareAndSet(current, newValue))
5 return current;
6 }
7 }
设置成功返回新值,设置失败返回旧值:public final boolean compareAndSet(int expect , int update)更新成功返回 true。java.util.concurrent.atomic 包使用无锁实现,性能高于一般的锁线程。此方法一般位于 unsafa 类中方法,保证了原子性。
JVM——锁的更多相关文章
- java多线程之:深入JVM锁机制2-Lock (转载)
前文(深入JVM锁机制-synchronized)分析了JVM中的synchronized实现,本文继续分析JVM中的另一种锁Lock的实现.与synchronized不同的是,Lock完全用Java ...
- 转:synchronized和LOCK的实现原理---深入JVM锁机制
JVM底层又是如何实现synchronized的? 目前在Java中存在两种锁机制:synchronized和Lock,Lock接口及其实现类是JDK5增加的内容,其作者是大名鼎鼎的并发专家Doug ...
- 深入JVM锁机制2-Lock
前文(深入JVM锁机制-synchronized)分析了JVM中的synchronized实现,本文继续分析JVM中的另一种锁Lock的实现.与synchronized不同的是,Lock完全用Java ...
- 深入JVM锁机制1-synchronized
目前在Java中存在两种锁机制:synchronized和Lock,Lock接口及其实现类是JDK5增加的内容,其作者是大名鼎鼎的并发专家Doug Lea.本文并不比较synchronized与Loc ...
- 终极锁实战:单JVM锁+分布式锁
目录 1.前言 2.单JVM锁 3.分布式锁 4.总结 =========正文分割线================= 1.前言 锁就像一把钥匙,需要加锁的代码就像一个房间.出现互斥操作的场景:多人同 ...
- JVM锁简介:偏向锁、轻量级锁和重量级锁
转自:https://www.aimoon.site/blog/2018/05/21/biased-locking/ 比较复杂,简略见另一篇:https://www.cnblogs.com/twohe ...
- [转载]深入JVM锁机制-synchronized
转自:http://blog.csdn.net/chen77716/article/details/6618779,并加上少量自己的理解 目前在Java中存在两种锁机制:synchronized和Lo ...
- [转帖]B4. Concurrent JVM 锁机制(synchronized)
B4. Concurrent JVM 锁机制(synchronized) https://www.cnblogs.com/zlxyt/p/11050346.html 挺好的 感觉这个文章写的 不过想要 ...
- java源码剖析: 对象内存布局、JVM锁以及优化
一.目录 1.启蒙知识预热:CAS原理+JVM对象头内存存储结构 2.JVM中锁优化:锁粗化.锁消除.偏向锁.轻量级锁.自旋锁. 3.总结:偏向锁.轻量级锁,重量级锁的优缺点. 二.启蒙知识预热 开启 ...
- JVM锁机制之synchronized
概述: synchronized是java用于处理多线程同步的一个关键字,用于标记一个方法/代码块,使之成为同步方法/同步块. 用synchronized可以避免多线程处理时的竞态条件问题. 相关概念 ...
随机推荐
- HDFS Shell 操作
HDFS Shell 操作 HDFS Shell 命令行格式 格式一:hadoop fs –命令名 参数 格式二:hdfs dfs –命令名 参数 HDFS 常用命令及参数 ls:查看 hdfs 中的 ...
- 【Appium_python】启动app,出现多次打开关闭导致失败问题,driver用单例模式(_new_)进行解决。
运用多设备,启动app多次出现打开又关闭问题,查看后是多次对driver进行实例化,就用单例的模式进行解决. 单例模式(Singleton Pattern)目的就是保证一个类仅有一个实例,每一次执行类 ...
- c++ 在项目中创建DLL,并调用
创建DLL分为两种方法,先介绍第一种 一.创建DLL (1) // dll.h #pragma once //dll.h #ifndef DLL_H_ #define DLL_H_ void prin ...
- PHP5-8各版本特性详解
汇总 PHP5.1: autoload PDO MySQLi 类型约束 PHP5.2: JSON 支持 PHP5.3: 命名空间 匿名函数 闭包 新增魔术方法__callStatic()和__invo ...
- 当前SAT主要关键技术及其相关文献2022-11-1
摘录自: Tasniem Nasser Al-Yahya, Mohamed El Bachir Menai, Hassan Mathkour:Boosting the Performance of C ...
- AES加密 php7版本 openssl_encrypt 遇到的坑
与前端对接api ,解密不了前端加密的数据. 问题描述: 1.前端用 cryptojs 加密的 密钥是24位 , 2.后端用的php7的 openssl_encrypt 同密钥来进行解密,发现解密 ...
- getElementsByClassName()获取不到值
在这种方式下,虽然使用了getElementsByClassName方法,但是并不能获得到值.从执行顺序上来说,在HTML还没有执行的时候JS就已经开始执行了,所以获得的值不能够获得到.因此,如果遇到 ...
- 查看mmdetection中模型的配置信息
方法一 可以直接打开mmdetection中的目录查看,/configs目录下都是对应的模型的配置 示例: 可以找到_base_目录下的这四个文件文件查看配置. 方法二 读取配置文件查看 在命令行中输 ...
- 利用shell脚本提高访问GitHub速度
Github由于做了域名限制,所以访问比较慢,编写了个脚本达到做本地域名解析提高GitHub的访问速度 #!/usr/bin/env bash # 该脚本用来提升github的访问速度 ROOT_UI ...
- 油猴CSDN净化脚本
CSDN版面越来越乱,最近还总是弹出红包雨和顶部巨大横幅,左侧也会随机出现学生认证弹窗.而且版面混乱难看,看起来非常费劲. 另外底下的推荐列表经常夹杂着CSDN文件下载的链接,下载文件又要付费,从来不 ...