JVM内核及原理、诊断与优化 - 锁

12 篇文章 2 订阅

介绍一下传统的锁是重量级的,monitorenter有可能让线程在OS层面挂起。

9.1 线程安全

  1. 多线程网站统计访问人数
    (1)使用锁,维护计数器的串行访问与安全性
  2. 多线程访问ArrayList
public class TestThreadCount {
    public static List<Integer> numberList = new ArrayList<Integer>();

    public static class AddToList implements Runnable {
        int startNum = 0;
        public AddToList(int startNumber) {
            startNum = startNumber;
        }

        @Override
        public void run() {
            int count = 0;
            while (count < 1000000) {
                numberList.add(startNum);
                startNum+=2;
                count++;
            }
        }
    }

    public static void main(String [] args) throws InterruptedException {
        Thread t1 = new Thread(new AddToList(0));
        Thread t2 = new Thread(new AddToList(1));
        t1.start();
        t2.start();
        while (t1.isAlive() || t2.isAlive()) {
            Thread.sleep(1);
        }
        System.out.println(numberList.size());
    }
}

在这里插入图片描述
可以发现,多线程网站统计访问人数,是否需要精确统计?如果不需要,可以不进行加锁。

9.2 对象头

(1)Mark Word,对象头的标记,32位。
(2)描述对象的hash,锁信息,垃圾回收标记,年龄。
a. 指向锁记录的指针
b. 指向monitor的指针
c. GC标记
d. 偏向锁线程ID

9.3. 偏向锁

(1)大部分情况是没有竞争的,所以可以通过偏向来提高性能。
(2)所谓偏向,就是偏心,即锁会偏向于当前已经占有锁的线程。
(3)将对象头Mark的标记设置为偏向,并将线程ID写入对象头Mark。
(4)当其他线程请求相同的锁时,偏向模式结束。
(5)-XX:+UseBiasedLocking JDK1.6中默认为启用。
(6)在竞争激烈的场合,偏向锁会增加系统负担。因为一个线程刚拿到锁,偏向模式可能就结束了。而导致系统不必要的操作浪费。

案例1:
在这里插入图片描述

9.4. 轻量级锁

基本对象锁。嵌入在线程栈中的对象。
在这里插入图片描述

(1)普通锁处理性能不够理想,轻量级锁是一种快速的锁定方法。
(2)如果对象没有被锁定
a. 将对象头的Mark指针保存到锁对象中。
b. 将对象头设置为指向锁的指针(在线程栈空间中)
在这里插入图片描述
(3)如果轻量级锁失败,表示存在竞争,升级为重量级锁(常规锁)
(4)在没有锁竞争的前提下,减少传统锁使用OS互斥量产生的性能损耗
(5)在竞争激烈时,轻量级锁会多做很多额外操作,导致性能下降
注意:判断一个线程是否持有轻量级锁,只要判断对象头的指针,是否在线程的栈空间范围内。

9.5. 自旋锁

 当竞争存在时,如果线程可以很快获得锁,那么可以不在OS层挂起线程,让线程做几个空操作(自旋)
 JDK1.6中-XX:+UseSpinning开启
 JDK1.7中,去掉此参数,改为内置实现
 如果同步块很长,自旋失败,会降低系统性能
 如果同步块很短,自旋成功率大大增加,节省线程挂起切换时间,提升系统性能

9.6. 偏向锁,轻量级锁,自旋锁总结

  1. 不是Java语言层面的锁优化方法
  2. 内置于JVM中的获取锁的优化方法或获取锁的步骤
    (1)偏向锁可用会先尝试偏向锁
    (2)轻量级锁可用会先尝试轻量级锁
    (3)以上都失败,尝试自旋锁
    (4)再失败,尝试普通锁,使用OS互斥量再操作系统层面挂起。

9.7. 减少锁的持有时间

持有时间长,自旋容易失败
public synchronized void syncMethod(){
        othercode1();
        mutextMethod();
        othercode2();
}
可换成:
public void syncMethod(){
        othercode1();
        synchronized(this){
            mutextMethod();
        }
        othercode2();
}

9.8. 减小锁粒度

粒度大,竞争激烈,偏向锁,轻量级锁失败概率就高。
a.将大对象,拆成小对象,大大增加并行度,降低锁竞争。
b.偏向锁,轻量级锁成功率提高。
c.看ConcurrentHashMap源码对锁的转换。
d.HashMap的同步实现,hashmap中维护了Entry<K,V>的数组。
-Collections.synchronizedMap(Map<K,V>m)
-返回SynchronizedMap对象
在这里插入图片描述
e. ConcurrentHashMap
- 若干个Segment:Segment<K,V>[] segments
- Segment中维护HashEntry<K,V>
- put操作时,先定位到Segment,锁定一个Segment,执行put。
f. 在减少锁粒度后,ConcurrentHashMap允许若干个线程同时进入。

9.9. 锁分离

对文件读取时,允许多个线程同时进入。
在这里插入图片描述

(1)根据功能进行锁分离
(2)ReadWriteLock
(3)读多写少的情况,可以提高性能(对文件读取时,允许多个线程同时进入)
(4)读写分离思想可以延伸,只要操作互不影响,锁就可以分离。
(5)LinkedBlockingQueue
-队列
-链表
在这里插入图片描述
take只作用于前端,put只作用于尾端。
E入队时,只要将D.last=E
A出队时,只要head=head.next
从功能的角度做分离,功能不同,互补影响,就可以分离
LinkedBlockingQueue实现中,可以使用takeLock和putLock两个锁。

9.10. 锁粗化

(1)通常情况下,为了保证多线程之间的有效并发,会要求每个线程持有锁的时间尽量短,即在使用完公共资源后,应该立即释放锁。只有这样,等待在这个锁上的其他线程才能尽早的获得资源执行任务。但是,凡事都有个度,如果对同一个锁不停的进行请求、同步和释放,其本身也会消耗系统宝贵的资源,反而不利于性能的优化。
在这里插入图片描述
在这里插入图片描述

9.11. 锁消除

(1)在即时编译时,如果发现不可能被共享的对象,则可以消除这些对象的锁操作。

在这里插入图片描述
锁不是由程序引入的,JDK自带的一些库,可能内置锁。栈上对象,不会被全局访问的,没有必要加锁。
在这里插入图片描述

9.12. 无锁

(1)锁是悲观的操作。
(2)无锁是乐观的操作。
(3)无锁的一种实现方式。
-CAS(Compare And Swap)
-非阻塞的同步
-CAS(V,E,N)
(4)在应用层面判断多线程的干扰,如果有干扰,则通知线程重试。
CAS算法的过程是这样:它包含3个参数CAS(V,E,N)。V表示要更新的变量,E表示预期值,N表示新值。仅当V值等于E值时,才会将V的值设为N,如果V值和E值不同,则说明已经有其他线程做了更新,则当前线程什么都不做。最后,CAS返回当前V的真实值。CAS操作是抱着乐观的态度进行的,它总是认为自己可以成功完成操作。当多个线程同时使用CAS操作一个变量时,只有一个会胜出,并成功更新,其余均会失败。失败的线程不会被挂起,仅是被告知失败,并且允许再次尝试,当然也允许失败的线程放弃操作。基于这样的原理,CAS操作即时没有锁,也可以发现其他线程对当前线程的干扰,并进行恰当的处理。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
课程简介: 目前,Java是最为流行的编程语言之一,它的基础平台就是JVM。除了Java,如JRuby、Scala、Clojure等语言也运行在JVM平台。熟悉和掌握JVM平台有着重要的实用价值和意义。 在本课程中个,将详细介绍JVM的基本原理、组成以及工作方式,并配合实际案例,介绍相关的调优技巧。 课程大纲: 第一课 初识JVM JVM分类 Java语言规范 JVM规范 介绍JVM的基本知识和发展历史,并介绍了Java语言规范和JVM规范。 第二课 JVM运行机制简介 堆、栈、方法区等 JVM启动流程 内存模型和volatile实例 解释和编译运行的概念 介绍JVM的内部结构、启动流程以及内存模型。并介绍JVM字节码的执行方式。 第三课 常用JVM参数 堆的分配参数 栈分配及实例讲解 server与client模式 调试跟踪参数 介绍常用的JVM参数,包括内存分配、堆栈分配、虚拟机运行模式以及调试跟踪参数。 第四课 GC的算法和种类 引用计数 标记清除 复制算法 标记压缩 可触及性 本章是理论性较强的一章,主要介绍GC的基本算法和思想,本章作为下一章节的前序课程。 第五课 GC控制参数 Serial ParNew等GC参数 GC的参数搭配实例分析 介绍GC的设置参数,并分析相关的案列。 第六课 类装载 class装载流程 ClassLoader模式 ClassLoader的使用实例分析 热替换例子 详细介绍ClassLoader的原理和应用。分析2个案例,说明ClassLoader的使用。 第七课 性能监控工具 线程死分析 OOM分析 介绍常用的JVM诊断和分析工具,并以死和OOM为例,展示这些工具的使用。 第八课 分析Java堆 MAT的使用案例 Jvisualvm介绍使用 介绍了Java堆的分析方法,以一个实例为基础,展示对堆的分析过程。 第九课 baise 轻量级 自旋 介绍JVM中对多线程的实现。 第十课 class文件结构 ASM库介绍 介绍JVM规范中的最重要的内容——Class文件结构,同时介绍ASM库的使用以及对class文件的修改。 第十一课 字节码执行 案例以及javap JIT及相关参数 介绍JVM的字节码以及反汇编方法,同时介绍JIT相关的参数和应用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值