各种锁

只作一个浅显的解释作为区分,具体深入还请搜索相关博客。

可重入锁(又叫递归锁)

synchronized(隐式,自动释放)、 lock(显式,需要手动解锁和上锁)
进入第一个锁之后,后面的锁都可以进入。

各种锁

可重入锁是一种特殊的互斥锁,它可以被同一个线程多次获取,而不会产生死锁。

  1. 首先它是互斥锁:任意时刻,只有一个线程锁。即假设A线程已经获取了锁,在A线程释放这个锁之前,B线程是无法获取到这个锁的,B要获取这个锁就会进入阻塞状态。
  2. 其次,它可以被同一个线程多次持有。即,假设A线程已经获取了这个锁,如果A线程在释放锁之前又一次请求获取这个锁,那么是能够获取成功的。

公平锁和非公平锁

非公平:new ReentrantLock() / ReentrantLock(false)
公平:new ReentrantLock( true)

  1. 公平锁:会考虑每个线程都会得到资源,不会饿死,但是效率慢
  2. 非公平锁:不考虑让别的线程获得资源,谁能抢到就是谁的,会导致别的线程饿死,效率高。

读写锁

ReentrantReadWriteLock:它表示两个锁,一个是读操作相关的锁,称为 共享锁;一个是写相关的锁,称为 排他锁

  1. 对共享资源有读和写的操作,读锁和写锁都会发生死锁。
    各种锁
  2. 读锁: 共享锁
  3. 写锁:独占锁。
  4. 特性:
(1)公平选择性:支持非公平(默认)和公平的锁获取方式,吞吐量还是非公
平优于公平。
(2)重进入:读锁和写锁都支持线程重进入。
(3)锁降级:遵循获取写锁、获取读锁再释放写锁的次序,写锁能够降级成为
读锁

锁降级

写锁降级为读锁。

各种锁

读锁不能升级为写锁。

悲观锁和乐观锁

图解

各种锁
  1. 悲观锁:每次都上锁,效率低
  2. 乐观锁:有个版本号,不同线程更改资源了,版本号不同会改失败。

CAS(Compare And Swap)就是将内存值更新为需要的值(包含三个操作数—— 内存位置的值(V)、预期原值(A)和新值(B)),但是有个条件,内存值V必须与期望值A相同。举个例子,内存值V、期望值A、更新值B,当V == A的时候将V更新为B。
参考

表锁、行锁

各种锁
  1. 表锁:操作记录是,对数据库整张表都上锁,会发生 死锁
  2. 行锁:对表中一条记录上锁。

自旋锁

当一个线程尝试去获取某一把锁的时候,如果这个锁此时已经被别人获取(占用),那么此线程就无法获取到这把锁,该线程将会等待,间隔一段时间后会再次尝试获取。

自旋锁在Java1.6中改为默认开启,并引入了自适应的自旋锁。
详情

适应性自旋锁

自适应意味着自旋的次数不在固定,而是由前一次在同一个锁上的自旋时间和锁的拥有者的状态共同决定。
如果在同一个锁对象上,自旋等待刚刚成功获得过锁,并且持有锁的线程正在运行中,那么虚拟机就会认为这次自旋也是很可能再次成功的,进而它将会允许线程自旋相对更长的时间。
如果对于某个锁,线程很少成功获得过,则会相应减少自旋的时间甚至直接进入阻塞的状态,避免浪费处理器资源。

详情参考

锁粗化

锁粗化就是告诉我们任何事情都有个度,有些情况下我们反而希望把很多次锁的请求合并成一个请求,以降低短时间内大量锁请求、同步、释放带来的性能损耗。

简单来说,就是把锁的范围放大,但是放大之后要比之前的性能效率好。这时候就可以粗化
场景:一个程序对同一个锁不间断、高频地请求、同步与释放,会消耗掉一定的系统资源,因为锁的讲求、同步与释放本身会带来性能损耗,这样高频的锁请求就反而不利于系统性能的优化了,虽然单次同步操作的时间可能很短。

public void doSomethingMethod(){
    synchronized(lock){
        //do some thing
    }
    //这是还有一些代码,做其它不需要同步的工作,但能很快执行完毕
    synchronized(lock){
        //do other thing
    }
}

//粗化
public void doSomethingMethod(){
    //进行锁粗化:整合成一次锁请求、同步、释放
    synchronized(lock){
        //do some thing
        //做其它不需要同步但能很快执行完的工作
        //do other thing
    }
}

参考

锁消除

不需要锁的情况,又要使用有锁的方法,这个时候用锁消除。


public class Demo {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        int size = 10000;
        for (int i = 0; i < size; i++) {
            createStringBuffer("Hyes", "&#x4E3A;&#x5206;&#x4EAB;&#x6280;&#x672F;&#x800C;&#x751F;");
        }
        long timeCost = System.currentTimeMillis() - start;
        System.out.println("createStringBuffer:" + timeCost + " ms");
    }
    public static String createStringBuffer(String str1, String str2) {
        StringBuffer sBuf = new StringBuffer();
        sBuf.append(str1);// append&#x65B9;&#x6CD5;&#x662F;&#x540C;&#x6B65;&#x64CD;&#x4F5C;
        sBuf.append(str2);
        return sBuf.toString();
    }
}

//append&#x6E90;&#x7801;&#x6709;&#x9501;&#xFF0C;&#x4F46;&#x5B9E;&#x9645;&#x4E0A;&#x5373;&#x4F7F;&#x5728;&#x5E76;&#x53D1;&#x73AF;&#x5883;&#x4E5F;&#x4E0D;&#x9700;&#x8981;
@Override
public synchronized StringBuffer append(String str) {
    toStringCache = null;
    super.append(str);
    return this;
}

代码中createStringBuffer方法中的局部对象sBuf,就只在该方法内的作用域有效,不同线程同时调用createStringBuffer()方法时,都会创建不同的sBuf对象,因此此时的append操作若是使> > 用同步操作,就是白白浪费的系统资源。这时我们可以通过编译器将其优化,将锁消除,前提是java必须运行在server模式(server模式会比client模式作更多的优化),同时必须开启逃逸分析:
-server -XX:+DoEscapeAnalysis -XX:+EliminateLocks
其中 +DoEscapeAnalysis表示开启逃逸分析, +EliminateLocks表示锁消除。
参考

偏向锁

各种锁
JDK6以上默认开启
当一个线程访问加了同步锁的代码块时,会在对象头中存储当前线程的 ID,后续这个线程进入和退出这段加了同步锁的代码块时,不需要再次加锁和释放锁。而是直接比较对象头里面是否存储了指向当前线程的偏向锁。如果相等表示偏向锁是偏向于当前线程的,就不需要再尝试获得锁了

参考

Original: https://www.cnblogs.com/CodeWater404/p/16338995.html
Author: CodeWater
Title: 各种锁

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/579504/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Spring Framework系统架构&学习路线

    核心容器-核心概念 posted @2022-07-05 21:30 yub4by 阅读(14 ) 评论() 编辑 Original: https://www.cnblogs.co…

    Java 2023年6月5日
    071
  • flowable 三种方式部署流程

    ​—————————————————————–自定义表单28. 定义模版:拖拽左侧表单元素到右…

    Java 2023年6月8日
    066
  • 设计模式之策略模式应用实例(Spring Boot 如何干掉 if else)

    需求 这里虚拟一个业务需求,让大家容易理解。假设有一个订单系统,里面的一个功能是根据订单的不同类型作出不同的处理。 订单实体: service接口: 传统实现 根据订单类型写一堆的…

    Java 2023年5月30日
    069
  • Starting MySQL… ERROR! The server quit without updating PID file 问题解决

    今天遇到一个mysql起不来,不知为啥挂了,启动是下面的报错Starting MySQL… ERROR! The server quit without updatin…

    Java 2023年6月5日
    054
  • 注释和良好的编程风格

    注释、良好的编程风格 注释 &#x5206;&#x7C7B;&#xFF1A; &#x5355;&#x884C;&#x6CE8;&am…

    Java 2023年6月7日
    066
  • Java 8新特性:新语法方法引用和Lambda表达式及全新的Stream API

    方法引用(Method references) 我们来看个接口和一个方法 <span class="hljs-keyword">public<…

    Java 2023年5月29日
    097
  • Java学习笔记

    本文主要记录了自己Java学习过程中的笔记,里面有很多Map、List、Set底层的源码解读,且知识点很全面,囊括从基本语法到IO编程、网络编程、并发编程以及反射等相关基础知识。此…

    Java 2023年6月16日
    061
  • 正则 捕获组之反向引用

    之前写正则的时候,经常用到 (.*?) 之类的用法.一般在替换的时候会用 $1 来引用括号里面匹配到的内容比如, 1.1.1.1 aaaa 2.2.2.2 bbbb 3.3.2.3…

    Java 2023年6月16日
    096
  • 十大排序算法

    冒泡排序 从数组头开始,比较相邻的元素。如果第一个比第二个大(小),就交换它们两个 对每一对相邻元素作同样的工作,从开始第一对到尾部的最后一对,这样在最后的元素应该会是最大(小)的…

    Java 2023年6月5日
    0100
  • vue3基础入门

    vue3基础入门 官方网站:https://v3.vuejs.org/ 中文文档: https://staging-cn.vuejs.org/guide/introduction….

    Java 2023年6月5日
    082
  • springboot线程

    (1)确保只有一个线程调用这个方法并且确保只创建一个HotSpot VM实例。因为HotSpot VM创建的静态数据结构无法再次初始化,所以一旦初始化到达某个确定点后,进程空间里就…

    Java 2023年5月30日
    084
  • 你见过哪些目瞪口呆的 Java 代码?

    自从毕业后,今年已经是我工作的第 8 个年头了,我甚至都快忘记了到底是哪年毕业的。 从出来,本人一直在做 Java 相关的工作,现在终于有时间坐下来,写一篇关于 Java 写法的一…

    Java 2023年5月29日
    079
  • android游戏妄撮java源码

    采用css+js实现 ==代码说明 index.html 程序加载运行的第一个页面,也是整个程序的入口 其它.html页面 程序中其它不同页面的内容信息界面 icon.png 用于…

    Java 2023年5月29日
    050
  • Notebook交互式完成目标检测任务

    摘要:本文将介绍一种在Notebook中进行算法开发的新方式,新手也能够快速训练自己的模型。 目标检测是计算机视觉中非常常用且基础的任务,但是由于目标检测任务的复杂性,往往令新手望…

    Java 2023年6月15日
    040
  • 设计模式在 Spring 框架中的良好应用

    在开始正文之前,请你先思考几个问题: 你项目中有使用哪些 GOF 设计模式 说一说 GOF 23 种设计模式的设计理念 说说 Spring 框架中如何实现设计模式 假设我是面试官问…

    Java 2023年5月30日
    066
  • 汇总数据库信息的存储过程

    问题: mysql日常开发过程中,数据库、表的很多信息分散在不同的工具和不同的界面中,来回切换查找非常麻烦。 解决方式: 基于这个问题,写了一个存储过程,将这些日常需要的信息集合在…

    Java 2023年6月9日
    078
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球