各种锁

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

可重入锁(又叫递归锁)

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/714575/

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

(0)

大家都在看

  • 如何本地navicat连接虚拟机安装的linux 的mysql

    2022.3.20 如何本地连接虚拟机安装的linux 的mysql 1防火墙开启开启 1.1、开启端口3306 1.2、重启防火墙 1.3查看已经开放的端口: 如果mysql 密…

    技术杂谈 2023年7月11日
    0102
  • 关于提问

    A 和 B 对话如下: A: xx 产品,一个月一个版本,只包含一个小功能,培训销售的工作跟不上怎么办?培训工作跟不上,研发做的功能前端都不知道,那做了有什么用?为什么不规划成大版…

    技术杂谈 2023年7月11日
    0117
  • prim算法求最小生成树

    例题链接 最小生成树的含义是,假设给定n个点,m条边(m > n – 1),在m条边中选择n – 1条边将n个点连接成一个连通图,即一棵生成树。因为每…

    技术杂谈 2023年6月21日
    0109
  • 服务器一般达到多少QPS比较好?

    服务器一般达到多少QPS比较好? 一个大型网站需要多少服务器? posted @2017-09-14 09:45 52php 阅读(6963 ) 评论() 编辑 Original:…

    技术杂谈 2023年5月31日
    0145
  • 一个简单的开源PHP爬虫框架『Phpfetcher』

    这篇文章首发在吹水小镇:http://blog.reetsee.com/archives/366 要在手机或者电脑看到更好的图片或代码欢迎到博文原地址。也欢迎到博文原地址批评指正。…

    技术杂谈 2023年5月31日
    0121
  • Java8 Stream 的最佳实践

    java8stream提供了对于集合类的流失处理,其具有以下特点: Lazy Evaluation(长度可以无限) 只能使用一次 内部迭代 Lazy Evaluation类似函数式…

    技术杂谈 2023年7月24日
    086
  • Docker — 从入门到实践PDF下载(可复制版)

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    技术杂谈 2023年7月10日
    098
  • macOS快捷键

    1. 最小化所有应用程序 command+option+h+m 2. 同应用窗口切换 command &#xFF5E; 3. 截图 "&#x5168;&a…

    技术杂谈 2023年6月21日
    0126
  • Mysql异地多活数据双向同步-CloudCanal实战

    异地多活是一项系统性工作,包含 web 层、应用服务层、数据层的流量分配和同步。 数据层的双向同步是整个方案基础,CloudCanal 在 MySQL 技术点 双向同步中, 暂时无…

    技术杂谈 2023年7月24日
    088
  • nginx禁止直接ip、未配置域名访问配置

    问题背景 最近偶然对线上域名配置的nginx IP进行直接访问后,发现http居然是可以通的,而https直接IP访问浏览器会报证书不安全的提示,点击详细查看发现是固定返回了ngi…

    技术杂谈 2023年6月21日
    0144
  • 利用 JS 脚本实现网页全自动秒杀抢购

    利用 JS 脚本实现网页全自动秒杀抢购 倒计时页面: 倒计时未结束时,购买按钮还不能点击。 结束时,可以点击购买,点击后出现提示”付款成功” 展示效果 GI…

    技术杂谈 2023年7月25日
    0101
  • 什么是回表,怎么解决?

    表tbl有a,b,c三个字段,其中a是主键,b上建了索引,然后编写sql语句SELECT * FROM tbl WHERE a=1这样不会产生回表,因为所有的数据在a的索引树中均能…

    技术杂谈 2023年7月25日
    095
  • JDK下载、安装与环境配置

    一、JDK下载与安装 1.1、下载JDK安装包 博主在这里给大家准备了一个64位操作系统的jdk1.8以便大家下载(使用的是迅雷)点击此处下载提取码:dfbt 如果其他小伙伴的电脑…

    技术杂谈 2023年6月21日
    0118
  • umeditor实现ctrl+v粘贴word图片并上传

    这种方法是servlet,编写好在web.xml里配置servlet-class和servlet-mapping即可使用 后台(服务端)java服务代码:(上传至ROOT/lqxc…

    技术杂谈 2023年5月31日
    0136
  • Java并发编程-线程池

    重点内容 线程池的使⽤ 创建线程池 提交任务 关闭线程池 线程池的原理 合理配置线程池 线程池的监控 1.线程池的创建 new ThreadPoolExecutor(corePoo…

    技术杂谈 2023年7月11日
    0103
  • RHCSA认证考试

    考试要求: 在 mars.domain250.example.com 上执行以下任务。○ 复查 ○ 完成 配置网络设置○ 复查 ○ 完成 配置您的系统以使用默认存储库○ 复查 ○ …

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