• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
haipali
博客园    首页    新随笔    联系   管理    订阅  订阅

Java中七七八八的各种锁

  1. 乐观锁 or 悲观锁
  • 乐观锁即蹲坑不锁门,只会在更新的时候判断有没有其他线程去更改数据,有的话就回滚
    典型:
  • 悲观锁即进厕所立马锁门,其他线程来了即阻塞,进到阻塞队列中,等待主线程蹲坑完毕后,按顺序获取
    典型:synchronized 和 ReentrantLock
  1. 独占锁 or 共享锁
  • 顾名思义,独占锁即获取之后,既能读数据也能写数据,其他线程无法获取锁且无法加任意类型的锁,即阻塞住了
  • 共享锁可同时被多个线程获取,但只能读数据,不能写数据
  1. 互斥锁 or 读写锁
  • 互斥锁保证了某一数据只能同时被一个线程操作,有唯一性 and 排他性
  • 读写锁:实际上为两个锁(读锁和写锁)读锁为共享锁,写锁为独占锁
  1. 公平锁 or 非公平锁
  • 公平锁指的是线程之间按照到来的先后顺序依次获取锁
  • 而非公平锁为,线程想获取锁了,无视队列,直接试图获取锁,强硬霸道,弱肉强食,弊端为有可能出现饥饿状态(即无线程成功获取到锁)
    Java中synchronized为非公平锁,ReentrantLock可设置,但默认为非公平锁
/**
* 创建一个可重入锁,true 表示公平锁,false 表示非公平锁。默认非公平锁
*/
Lock lock = new ReentrantLock(false);
  1. 可重入锁
    我之前一直以为只有Redis可以实现,现在知道了ReentrantLock(可重入锁)顾名思义,synchronized也可以实现
    好处:实现递归取锁,避免死锁,体现灵活性与可控性
  2. 自旋锁
    个人理解:尝试获取锁,获取不到的话不会直接将线程挂起,而是采取自旋操作,进入一个忙循环,循环尝试获取锁
    好处:减少线程被挂起的几率,因为线程被挂起需要耗费大量的资源,但是长时间进入忙循环会耗费比挂起更高的性能,所以不适合长时间自旋的场景
  3. 分段锁 Segment
    即更细粒度的锁,当需要进行数据操作的时候,只要对需要操作的模块加锁即可,不用对整体加锁
    CurrentHashMap底层就使用的Segment
  4. 锁升级
    即无锁 --》 偏向锁 --》轻量级锁 --》 重量级锁
  • 偏向锁:会偏向第一个访问的线程,通常在不存在多线程竞争的时候默认上这把锁,后续若访问该资源的线程过多,通过控制对象Mark Word的标志位来判断,对象头存储的线程id是否与当前线程id一致
  • 轻量级锁:竞争变得较为激烈,即升级为轻量级锁,承认线程竞争的存在,但一般压力较小,获取不到会采用自旋锁那一套
  • 重量级锁:自旋超过一定次数,即升级为重量级锁,没获取到的阻塞,去阻塞队列排队
  1. 锁优化
  • 锁粗化
    顾名思义,将多个小锁合并为一个大锁,锁范围扩大
    粗化前
private static final Object LOCK = new Object();

for(int i = 0;i < 100; i++) {
    synchronized(LOCK){
        // do some magic things
    }
}

粗化后

 synchronized(LOCK){
     for(int i = 0;i < 100; i++) {
        // do some magic things
    }
}

应该很好理解吧。。。。

  • 锁消除
    即已上锁的线程,虚拟机编译器在运行时检测到几乎没啥线程竞争了,从而将锁消除
    例子:方法test本身就为线程安全的
public String test(String s1, String s2){
    StringBuffer stringBuffer = new StringBuffer();
    stringBuffer.append(s1);
    stringBuffer.append(s2);
    return stringBuffer.toString();
}

StringBuffer的线程安全在此场景显得有点点多余,故虚拟机自动进行了锁消除

StringBuffer.class

// append 是同步方法
public synchronized StringBuffer append(String str) {
    toStringCache = null;
    super.append(str);
    return this;
}

最后,即大佬总结的神图

posted @ 2023-07-17 22:05  蓝光水母  阅读(23)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3