当前位置: 首页 > news >正文

Java多线程进阶(锁策略)

常见的锁策略:

1)悲观锁和乐观锁

加锁的时候,预测当前锁冲突的概率是大还是小。

预测当前锁冲突概率大,后续要做的工作往往就会更多,加锁开销就更大(时间,系统资源)=》悲观锁

预测当前锁冲突概率不大,后续要做的工作往往就更少,加锁开销就更小(时间,系统资源)=》乐观锁

synchronzied既是乐观锁,也是悲观锁

synchronzied支持自适应,能够自动的统计出当前的锁冲突的次数,进行判定当前锁冲突概率低还是概率高。

当冲突概率低的时候,就是按照乐观锁的方式来执行的。(速度更快)

当锁冲突概率高的时候,就会升级悲观锁的方式来执行(做的工作更多)

2)重量级锁和轻量级锁

一般来说,悲观锁,往往就是重量级锁,乐观锁就是轻量级锁。

3)自旋锁和挂起等待锁

自旋锁,是轻量级锁的一种典型的实现方式

消耗了更多的cpu资源,但是一旦锁被释放,就能第一时间拿到锁。拿到锁的速度很快。消耗cpu

挂起等待锁,是重量级锁的一种典型实现方式。

借助系统中的线程,当尝试加锁,并且锁被占用了,出现锁冲突,就会让当前这个尝试加锁的线程,被挂起(阻塞状态)此时这个线程就不会参与调度了。

直到这个线程被释放了,然后系统才能唤醒这个线程,去尝试重新获取锁。

(拿到锁的速度更慢,节省cpu)

synchronzied 轻量级锁部分,基于自旋锁实现,重量级锁部分,基于挂起等待锁实现。(基于CAS机制来实现)

4)可重入锁和不可重入锁

synchronzied就是可重入锁,一个线程,针对这把锁,连续加锁两次,不会死锁。

比如c++de std::mutex就是不可重入锁,一个线程针对这把锁,连续加锁两次,会死锁。

5)公平锁和非公平锁

公平锁:严格按照先来后到的顺序来获取锁,哪个线程等待的时间长,哪个线程就拿到锁。

非公平锁:若干个线程,各凭本事,随机的获取到锁,和线程等待时间无关了。

synchronized属于非公平锁,多个线程,尝试获取这个锁,此时是按照概率均等的方式来进行获取的。

{系统本身线程调度的顺序就是随机的,如果需要实现公平锁,就需要引入额外的队列,按照加锁顺序把这些获取锁的线程的线程入队列,再一个一个的取}

6)互斥锁和读写锁

synchronized本事就是普通的互斥锁

读写锁,则是一个更特殊的锁(加读锁,加写锁,解锁)

Java的读写锁是这样设定的:

1)读锁和读锁之间,不会产生互斥

2)写锁和写锁之间,会产生互斥

3)读锁和写锁之间,会产生互斥

synchronized实现原理:

synchronized即使悲观锁也是乐观锁,既是轻量级锁也是重量级锁,轻量级锁是自旋转实现,重量级锁就是挂起等待锁实现,是可重入锁,不是读写锁,是非公平锁

synchronized的“自适应”

锁升级的过程:

1)未加锁的状态(无锁){代码中开始调用执行synchronized}

2)偏向锁

3)轻量级锁

4)重量级锁

偏向锁的过程:

首次使用synchronized对对象进行加锁的时候,不是真的加锁,而只是做一个“标记”(非常轻量,非常快,几乎没有开销)

如果没有别的线程尝试对这个对象加锁,就可以保持这个暧昧状态,一直到解锁。

但是,如果在偏向锁状态下,有某个线程也尝试来对这个对象加锁,立马把偏向锁升级成轻量级锁(真的加锁,真的有互斥了),相当于立即确认关系,也就可以保证锁能够正常生效。

本质上,偏向锁策略就是“懒”字具体体现,能不加锁,就不加锁,能晚加锁,就晚加锁。

针对一个锁对象来说,是不可逆的,只能升级,不能降级。

一旦升级到了重量级锁,不会回退到轻量级锁(当前jvm里面的做法)

7)锁消除,编译器优化策略

代码里面写了加锁操作,jvm会对当前的代码做出判定,看这个地方到底是不是真的需要加锁。

如果这个不需要加锁,就会自动的把加锁操作,给优化掉。

8)锁粗话,也是一种优化策略

有些逻辑中,需要频繁加锁频繁,编译器就会自动的把多次细粒度的锁,合并成一次粗粒度的锁。

三个简单的加锁操作,合并成一个加锁操作。

CAS compare and swap 比较和交换

这是一条cpu(原子的)指令,就可以完成比较和交换这样的一套操作下来。

CAS的流程==》想象成一个方法

  boolean cas(address,reg1,reg2){if(address ==reg1){}//把address内存地址的值和reg2寄存器的值进行交换return true;}return false;

这里说的交换,实际上更多的是来“赋值”,一般更关心内存中,交换后的数据。而不关心reg2寄存器里交换后的数据。

由于cpu提供了上述指令,因此操作系统内核,也就能够完成上述操作,就会提供出这样的CAS的api,jvm又对于系统的CASapi进一步的封装了,在Java代码中也就可以使用CAS操作了。

在Java中也有一些类,对cas进行了进一步的封装,典型的就是原子类。

Atomiclnteger相当于针对int进行了封装。可以保证此处的++--操作是原子的

package Thread;import java.util.concurrent.atomic.AtomicInteger;public class Demo25 {private static AtomicInteger count=new AtomicInteger(0);public static void main(String[] args) throws InterruptedException {Thread t=new Thread(() ->{for (int i = 0; i <10000 ; i++) {count.getAndIncrement();}});Thread t1=new Thread(() ->{for (int i = 0; i < 50000; i++) {count.getAndIncrement();}});t.start();t1.start();t.join();t1.join();System.out.println(count);}
}

CAS的ABA问题

CAS这里的核心是比较-发现相等-交换=》潜台词 发现相等=》数据中间没有发生过任何改变


http://www.mrgr.cn/news/71910.html

相关文章:

  • MySQL数据库专栏(四)MySQL数据库链接操作C#篇
  • 用MVVM设计模式提升WPF开发体验:分层架构与绑定实例解析
  • 杨中科 .Net Core 笔记 DI 依赖注入2
  • 推荐一个超漂亮ui的网页应用设计
  • Note1: Linux 多进程服务器端
  • 明日周刊-第27期
  • python 由于系统缓冲区空间不足或队列已满,不能执行套接字上的操作
  • 政务数据治理专栏开搞!
  • 时间空间频域融合的Corssformer时间序列预测项目
  • Fortran安装(vscode+gcc+Python)
  • Django Form
  • JVM——类加载器、类加载器的分类
  • 专题十八_动态规划_斐波那契数列模型_路径问题_算法专题详细总结
  • 2024134读书笔记|《花间集》——云解有情花解语,山月不知心里事, 水风空落眼前花
  • SpringBoot如何集成WebSocket
  • RT-DETR融合NeurIPS[2022]Ghost Module v2模块
  • C#-命名空间
  • 【FFmpeg】FFmpeg 函数简介 ③ ( 编解码相关函数 | FFmpeg 源码地址 | FFmpeg 解码器相关 结构体 和 函数 )
  • (一)- DRM架构
  • 【364】基于springboot的高校科研信息管理系统
  • WSL2 中大模型环境一步到位!(wsl --update 不好使/wsl2安装/python环境)
  • springboot基于Java的小区物业智能卡管理的设计与实现,计算机毕业设计项目源码311,计算机毕设程序(LW+开题报告、中期报告、任务书等全套方案)
  • 「 审稿答复 」如何写Response评论回复的“第一句”
  • 抖音电商发布双11数据:275个品牌通过直播带货实现成交额过亿元
  • Hyper-v中ubuntu与windows文件共享
  • keras实现道路裂缝检测