介绍几个常见的锁策略!

简介: 介绍几个常见的锁策略!

到此为止:多线程大致结束!!

多线程初阶,主要介绍了线程的概念,及其多线程编程,多线程编程的注意事项,代码案列,算是最核心的部分了!!

面试常考+工作常用!!

多线程进阶(锦上添花),学有余力可以搞搞!!(能理解最好,不能理解就背)

主要围绕一些更深奥的面试题来展开的!

常见的锁策略:

策略:解决问题的方法:

锁策略:实现一把锁,你有哪些具体的实现方式??

正常程序员是不需要掌握的,谁去要实现一把锁,谁就去掌握!

实际上,在开发中,绝大多数程序猿究其一生都没有机会实现一把锁,但是,Java面试要考,所以Java程序员就得知道~~

一下介绍的几个锁策略,不只是针对Java程序员的,别的语言,别的工具,只要涉及到锁,也同样适用!!

1.乐观锁VS悲观锁

锁的实现者预测接下来锁冲突的概率是大??还是不大??根据冲突的概率,来决定接下来该咋做??

锁冲突就是锁竞争,两个线程针对一个对象加锁,产生阻塞等待了

乐观锁:预测接下来冲突概率不大

悲观锁:预测接下来锁冲突概率比较大

导致最终所作的事情不一样!!

通常来说:(并不绝对)

悲观锁一般要做的工作要多一些,效率会更低一些

乐观锁做的工作会更少一点,效率会更高一些

2.重量级锁VS轻量级锁

轻量级锁:加锁解锁过程更加高效

重量级锁:加锁解锁过程更加低效

轻量级锁and重量级锁和乐观锁悲观锁虽然不是一回事,但是确实有一定的重合!

一个乐观锁很有可能是一个轻量级锁(不绝对)

一个悲观锁很有可能是一个重量级锁(不绝对)

3.自旋锁VS挂起等待锁

自旋锁是轻量级锁的一种典型实现!

通常是纯用户态的,不需要经过内核(时间相对更短)

一旦锁被释放,就能第一时间拿到锁,速度会更快!(忙等,消耗CPU资源)

挂起等待锁是重量级锁的一种典型实现!

通过内核的机制来实现挂起等待(时间更长了!)

如果锁被释放,不能第一时间拿到锁的,可能需要过很久才能拿到锁!!这个时间是空闲出来的,可以趁机学点其他技能!!

基于上述三组所策略:synchronized这把锁,属于哪种呢??

synchronized既是悲观锁,也是乐观锁,既是轻量级锁,也是重量级锁。

轻量级锁部分基于自旋锁实现,重量级锁部分基于挂起等待锁实现!!

synchronized会根据当前锁竞争的激烈程度自适应!!

如果锁冲突不激烈,以轻量级锁/乐观锁的状态运行!!

如果锁冲突激烈,以重量级锁/悲观锁的状态运行!!

4.互斥锁VS读写锁

synchronized是互斥锁,加锁就只是单纯的加锁,没有更细的区分了!!

像synchronized只有两个操作:

  1. 进入代码块加锁
  2. 出了代码块解锁

除了这个之外,还有一种读写锁,能够把读和写两种加锁区分开:

读写锁:

  • 给读加锁
  • 给写加锁
  • 解锁

如果多个线程读同一个变量,捕获涉及到线程安全问题

读写锁中的约定:

  1. 读锁和读锁之间,不会锁竞争,不会产生阻塞等待(不会影响程序的速度,代码还是跑的很快)。
  2. 写锁和写锁之间,有锁竞争,减慢速度,但是保证准确性
  3. 读锁和写锁之间,也有锁竞争,减慢速度,但是保证准确性

读写锁,更适用于一写多读的情况!!

标准库提供了另外两种专门的读写锁(读锁是一个类,写锁是一个类)

类很麻烦,若需要使用,稍微查一下就知道该如何使用了!!

5.可重入锁VS不可重入锁

如果一个锁,在一个线程中,连续对该锁咔咔加锁两次,不死锁就叫做:可重入锁,如果死锁(没人去解锁)了,就叫不可重入锁!!

上述的锁策略都挺抽象的,能理解最好,不能理解就背下来,面试乐意考(Java开发方向乐意考,测试开发方向不太考)

Object locker=new Object();
    synchronized (locker){
        synchronized (locker){
        }
    }

形如这种代码,就是加锁两次的情况~~

第二次尝试加锁,需要等待第一个锁释放,第一个锁释放,需要等待第二个锁加锁成功!!

逻辑上矛盾《——》死锁了!!对于这种代码,日常开发中很容易接触到!!

class BlockingQueue{
    synchronized void put(int elem){
        this.size();
        //………………其他代码
    }
    synchronized int size(){
        //……………………其他代码
    }
}

这个代码都是针对this加锁,这种代码非常常见,难道针对会死锁吗??实际上在Java中并不会!!

synchronized是一个可重入锁,在这个场景下不会死锁(加锁的时候判定一下)看当前尝试申请锁的线程是不是已经就是锁的拥有者了!!(如果是直接放行!!)

关于死锁的情况:

1.一个线程,一把锁

可重入锁没事,不可重入锁死锁

2.两个线程两把锁,即使是可重入锁也会死锁

Object locker1=new Object();
    synchronized (locker1){
        synchronized (locker2){
        }
    }
    Object locker2=new Object();
    synchronized (locker2){
        synchronized (locker1){
        }
    }

京东案列:

疫情期间:一码通崩溃了:

程序员:保安兄弟,你让我进去修Bug

保安:你得出示一码通才能进

程序员:我进去修Bug,才能去出示一码通

保安:不行,得出示一码通才能进

………………………………

3.N个线程,M把锁

当线程数量和锁数量不对等,就更容易死锁了!!

死锁的四个必要条件!!(缺一不可)

  1. 互斥使用:一个线程拿到一把锁之后,另一个线程不能使用(锁的基本特点)
  2. 不可抢占:一个线程拿到锁,只能自己主动释放,不能是被其他线程强行占有,挖墙脚是不行的!(锁的基本特点)
  3. 请求和保持:吃着碗里的,惦记着锅里的!!追到了1号女神之后,又对2号女神跃跃欲试,但是此时仍然不会放弃1号女神的(代码的特点)
  4. 循环等待:逻辑依赖循环的:钥匙锁车里了,车钥匙锁家里了(代码的特点)

死锁是一个比较严重的Bug,那么,实践中如何避免死锁呢??

一个最简单有效的办法:破解循环等待这个条件!!

做法:针对锁进行编号,如果需要同时获取多把锁,约定加锁顺序,务必是先对小的编号加锁,后对大的编号加锁!!只要约定了加锁顺序,循环等待自然破除,死锁也就不会形成了!!

上述关于死锁的内容,既是开发中常见的问题,又是面试中的经典问题!

Object locker1=new Object();
    synchronized (locker1){
        synchronized (locker2){
        }
    }
    Object locker2=new Object();
    synchronized (locker1){
        synchronized (locker2){
        }
    }

把加锁顺序调整为约定顺序即可!!

约定:先加锁小的编号,后加锁大的编号!!此时只要所有线程都遵循这个顺序即可!!

6.公平锁VS非公平锁(重点掌握一下)

约定:

遵循先来后到就是公平锁!

不遵循先来后到就是非公平锁!

(等概率竞争是不公平的!!)

系统对于线程的调度是随机的,自带的synchronized这个锁是非公平的!要想实现公平锁,需要在synchronized的基础上,加个队列,来记录这些加锁线程的顺序!!

synchronized特点

  • 既是乐观锁,也是悲观锁
  • 既是轻量级锁,也是重量级锁
  • 轻量级锁基于自旋实现,重量级锁基于挂起等待实现
  • 不是读写锁
  • 是可重入锁
  • 是非公平锁
相关文章
|
存储 关系型数据库 MySQL
centos部署mysql8
centos部署mysql8
542 0
|
8月前
|
资源调度 前端开发 JavaScript
React的测试:使用Jest和React Testing Library进行深入探索
【4月更文挑战第25天】本文探讨了使用Jest和React Testing Library进行React测试的方法。Jest是Facebook推出的JavaScript测试框架,适合React测试,提供全面的API和功能。React Testing Library侧重于组件行为,提倡按用户交互方式测试。安装这两个工具后,可通过编写测试用例(如模拟点击事件)来验证组件功能。运行Jest可执行测试并显示结果。此外,还介绍了高级测试技巧和模拟功能,强调了它们对于确保组件正确性、提升开发效率的重要性。
|
NoSQL 关系型数据库 索引
从一个案例深入剖析InnoDB隐式锁和可见性判断(1)
从一个案例深入剖析InnoDB隐式锁和可见性判断
从一个案例深入剖析InnoDB隐式锁和可见性判断(1)
|
SQL 关系型数据库 MySQL
从一个案例深入剖析InnoDB隐式锁和可见性判断(2)
从一个案例深入剖析InnoDB隐式锁和可见性判断
从一个案例深入剖析InnoDB隐式锁和可见性判断(2)
|
JavaScript
【Vue3从零开始-实战】S7:注册页面布局开发及路由串联
【Vue3从零开始-实战】S7:注册页面布局开发及路由串联
320 0
【Vue3从零开始-实战】S7:注册页面布局开发及路由串联
|
域名解析
用vitepress搭建文档网站踩坑记录
之前用 vuepress2 + vite 成功搭建了一个博客网站,这不 vue3 的文档改用 vitepress 搭建的,看着挺好看的,就想着也来折腾折腾搭建一个工作的文档网站
156 0
|
关系型数据库 MySQL 索引
从一个案例深入剖析InnoDB隐式锁和可见性判断(3)
从一个案例深入剖析InnoDB隐式锁和可见性判断
123 0
|
存储 关系型数据库 MySQL
从一个案例深入剖析InnoDB隐式锁和可见性判断(4)
从一个案例深入剖析InnoDB隐式锁和可见性判断