【JavaEE】多线程进阶

news2024/11/23 12:40:35

在这里插入图片描述
🤡🤡🤡个人主页🤡🤡🤡
🤡🤡🤡JavaEE专栏🤡🤡🤡

文章目录

  • 1.锁策略
    • 1.1悲观锁和乐观锁
    • 1.2重量级锁和轻量级锁
    • 1.3自旋锁和挂起等待锁
    • 1.4可重入锁和不可重入锁
    • 1.5公平锁和非公平锁
    • 1.6互斥锁和读写锁
  • 2.synchronized的实现原理
    • 2.1实现过程
    • 2.2偏向锁
    • 2.3优化策略
      • 2.3.1锁升级
      • 2.3.2锁消除
      • 2.3.3锁粗化
  • 3.CAS
    • 3.1什么是CAS
    • 3.2CAS的应用
      • 3.2.1原子类
      • 3.2.2实现自旋锁
    • 3.3CAS的ABA问题
      • 3.3.1ABA问题
      • 3.3.2ABA引起的BUG
      • 3.3.3如何避免由ABA问题引起的BUG
  • 4.java.util.concurrent常见类
    • 4.1Callable接口
    • 4.2ReentrantLock类
      • 4.2.1发音
      • 4.2.2ReentrantLock的关键特性与功能
    • 4.3Semaphore类——信号量

1.锁策略

1.1悲观锁和乐观锁

区别:加锁的时候,预测当前锁冲突的概率是大还是小
悲观锁:预测当前锁冲突概率大,后续要做的工作往往就会更多,加锁的开销(时间,系统资源)就更大。
乐观锁:预测当前锁冲突概率小,后续要做的工作往往就会更少,加锁的开销(时间,系统资源)就更小。
注意:悲观锁往往通过内核来完成操作的,所以做的工作多,乐观锁往往通过用户完成操作的,所以做的工作少。

1.2重量级锁和轻量级锁

这两个锁和上述的悲观锁和乐观锁有着很大的关系,一般悲观锁就是重量级锁,因为悲观锁做的任务多,那么就需要很大的开销所以就是重量级锁,反之乐观锁做的任务少,那么开销就少那么就是轻量级锁。
注意:一般这两个锁和上述两个锁都混着用,之所以有区别是因为出发点不一样。

1.3自旋锁和挂起等待锁

自旋锁:锁未被释放之前,cpu会一直空转和忙等,但等锁被释放之后就会立马获取到锁,自旋锁是实现轻量级锁典型的案例。
挂起等待锁:当出现锁冲突,那么要加锁的这个线程就会被挂起等待,此时的线程就不会参与调度,直到这个锁被释放,然后系统内核才唤醒这个线程,去尝试获取锁,拿锁的速度很慢,挂起等待锁是实现重量级锁的典型案例。

1.4可重入锁和不可重入锁

可重入锁:针对一个线程,可以连续加锁两次不会出现死锁,synchronized就是可重入锁。
不可重入锁:针对一个线程,连续加锁两次会出现死锁。像c++中的std::mutex就是不可重入锁

1.5公平锁和非公平锁

公平锁:严格按照先来后到的顺序来获取锁,哪个线程等待的时间长,哪个线程就拿到锁
非公平锁:若干个线程,各凭本事,随机获取锁和线程等待时间无关,synchronized就是一个非公平锁。
系统调度本来就是随机的,如果想实现一个公平锁,那么就需要引入一个特殊的队列来根据线程等待的时间来出队列。

1.6互斥锁和读写锁

互斥锁:加锁和解锁,synchronized就是一个互斥锁
读写锁:加读锁和加写锁,读与读之间不会产生互斥,写与写之间会产生互斥,读与写之间也会产生互斥,

2.synchronized的实现原理

2.1实现过程

未加锁(无锁状态)——>偏向锁——>轻量级锁——>重量级锁

2.2偏向锁

首次使用sychronized对对象加锁,此时并不是真正的加锁而是做了一个标记(非常轻量非常快,几乎没有开销)
如果在这其中没有其他线程对这个对象加锁,那么就一直保持这样的一个状态直到解锁的时候(解锁也只是修改标记,几乎没有开销)
但是如果在这期间,有其他对象对进行加锁,那么就会立马升级成轻量级锁。
注意:在这个升级过程中,是不可逆的,一旦升级了就不能降级(在目前版本的JVM中)

2.3优化策略

2.3.1锁升级

2.3.2锁消除

当你在这个代码中加了锁,编译器和JVM会检查当前代码需不需要加锁,不需要就会将这个锁帮你消除,这是在内部操作的,程序猿是感知不到的,例如我在单线程中加了锁,那么编译器和JVM就会把这个锁给消除。

2.3.3锁粗化

在有些逻辑中,需要频繁加锁和解锁,编译器就会自动把这些多次细粒度的锁合成一次粗粒度的锁,"粒度"是指加锁范围中代码的多少,代码越多粒度就越粗,反之越细。

3.CAS

3.1什么是CAS

CAS这是一个比较交换指令,而且这一指令详细的就是读取内存,比较是否相等,修改内存三个步骤,而与之前线程安全问题中多个线程对同一个变量进行修改操作是一样的,也是上述这三个步骤,但CAS这三个步骤是打包一起的是原子的,而对变量修改不是原子的,所以需要加锁,才能保证线程安全,所以CAS在某种程度也可以实现锁的功能。

3.2CAS的应用

3.2.1原子类

标准库中提供了 java.util.concurrent.atomic 包, ⾥⾯的类都是基于这种⽅式来实现的. 典型的就是 AtomicInteger 类.
在这里插入图片描述
通过方法来实现变量的算术运算

//count++
count.getAndIncrement();
//++count
count.incrementAndGet();
//count--
count.getAndDecrement();
//--count
count.decrementAndGet();
//count += 10
count.getAndAdd(10);

通过CAS实现两个线程对count变量相加

AtomicInteger count = new AtomicInteger(0);
Thread t1 = new Thread(()-> {
    for (int i = 0; i < 50000; i++) {
        count.getAndIncrement();
    }
});
Thread t2 = new Thread(()-> {
    for (int i = 0; i < 50000; i++) {
        count.getAndIncrement();
    }
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("count = " + count);

这种编程方式也称为无锁编程,这种方式可以提高效率,但适用范围不大。

3.2.2实现自旋锁

由于在读取内存,比较相等,修改内存这三个步骤是原子,所以在某种程度上可以实现锁的功能
以下就是一段伪代码简单实现一下自旋锁

class SpinLock{
    public Thread owner = null;
    public void lock() {
        while(!CAS(this.owner,null,Thread.currentThread())) {
            
        }
    }
    public void unlock() {
        this.owner = null;
    }
}

在这里插入图片描述
在这个方法中,owner变量如果是null,那么此时就是未加锁状态,那么CAS方法就返回true,取反则为false,退出循环,此时就是未加锁,但当owner这个变量不为null的时候调用CAS方法的线程就会将该对象的引用赋值给this.owenr,这个操作是原子的,所以不会有线程安全问题,从侧面就可以体现相当于对这个线程加锁,从此实现了锁的功能。

3.3CAS的ABA问题

3.3.1ABA问题

假设此时我有两个线程分别为t1和t2线程,还有一个共享变量num,此时t1线程想要通过CAS编码的形式把num原来的值A改为C,但是在这个操作之中,t2线程将num中的值从A改为B,又从B改为A,但是在这期间,t1线程不知道。

3.3.2ABA引起的BUG

比如,有一个cs中的悍匪玩家刚好现在余额还有17元买一个钥匙开个箱子,在购买的过程中,因为网络的波动,他按了两次购买键,导致启动了两个线程去完成这个任务,那么t1线程在购买的时候,t2线程在等待阻塞,t1线程完成了购买获得了一把钥匙,但在t2线程执行前刚好另一个ct好友给这位悍匪玩家充值了17元,导致t2线程在执行的时候发现余额中还有17元,则又帮悍匪玩家买了一把,此时就出现了bug,这个时候买了两把钥匙就是ABA问题引起的。

3.3.3如何避免由ABA问题引起的BUG

在要修改的值加入一个版本号,在CAS比较数据的时候比较当前值和之前要修改的值,也要比较版本号是否相同
CAS操作在读取之前要修改的值的同时,也要读取版本号
真正到修改的时候:
如果当前读的版本号与之前要修改的版本号相同,则修改数据并且修改版本号
如果当前读的版本号与之前要修改的版本号不相同,则视为修改失败(可以认为该数据被修改过)
将这个思路带到悍匪玩家买钥匙的案例中:

  • 购买钥匙需要17余额,t1和t2线程获取的余额是17,版本号都是1
  • t1线程购买成功之后,余额变为0,版本号变为2,此时t2线程再阻塞等待
  • 在t2线程执行前,悍匪玩家的好友ct兄弟为其充值了17元余额,此时账户余额又变为17,但此时版本号为3
  • 到t2线程执行的时候,发现余额是17,和之前读到的余额是一样,但是版本号不一样,第一次读的是1,但现在读的是3,版本号不同,则可以视为操作失败

4.java.util.concurrent常见类

4.1Callable接口

这个接口里有个叫call()方法,这个方法与Runnable接口中的run()方法其实大同小异,只是前者有个返回值
在Java中,Thread类的构造方法不直接接受一个Callable对象作为参数。,则需要另一个类来作为一个媒介,FutureTask作为一个媒介。

public static void main(String[] args) throws InterruptedException, ExecutionException {
    Callable<Integer> callable = new Callable<Integer>() {
        int sum = 0;
        @Override
        public Integer call() throws Exception {
            for (int i = 1; i < 1000; i++) {
                sum += i;
            }
            return sum;
        }
    };
    FutureTask<Integer> futureTask = new FutureTask<>(callable);
    Thread thread = new Thread(futureTask);
    thread.start();
    thread.join();
    System.out.println("sum = " + futureTask.get());
}

4.2ReentrantLock类

4.2.1发音

ReentrantLock

4.2.2ReentrantLock的关键特性与功能

  1. ReentrantLock提供了公平锁的实现
  2. ReentrantLock提供了tryLock操作,该操作会立即返回,是否获取到锁,这对于避免死锁和实现超时机制非常有用。
  3. ReentrantLock搭配了Condition类完成等待通知,Condition比wait和notify更强点,Condition可以指定阻塞线程唤醒

4.3Semaphore类——信号量

信号量就是一个计数器,计系统资源的个数。
在底层中对信号量的两个基本操作分别为P操作和V操作
P操作——申请资源
V操作——释放资源
在java中JVM将这两个操作分别封装成acquire(申请资源),release(释放资源)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1904782.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

分子AI预测赛Task4笔记(结束)

话不多说&#xff0c;直接上官方链接&#xff1a;‌​​​‍&#xfeff;​⁠​‌​‍​​&#xfeff;​‌​⁠‬​&#xfeff;‬​​‌​​​​‬‬​​​​‍⁠‍‌​&#xfeff;⁠Task3&#xff1a;进阶baseline详解 - 飞书云文档 (feishu.cn)Task4&#xff1a;持续尝试&…

嵌入式C语言面试相关知识——内存管理(不定期更新)

嵌入式C语言面试相关知识——内存管理&#xff08;不定期更新&#xff09; 一、博客声明二、自问题目1、嵌入式系统的内存布局是怎么样的&#xff1f;2、动态内存分配在嵌入式系统中的使用有什么注意事项&#xff1f;3、什么是内存碎片&#xff0c;如何减少内存碎片&#xff1f…

用ThreadLocal解决线程隔离问题

存在的以下代码所示的线程隔离问题&#xff1a; package study.用ThreadLocal解决线程隔离问题;/*线程隔离 - 在多线程并发场景下&#xff0c;每个线程的变量都应该是相互独立的线程A&#xff1a;设置&#xff08;变量1&#xff09; 获取&#xff08;变量1&#xff09;线程B&a…

【数据结构与算法】希尔排序

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;《数据结构与算法》 期待您的关注 ​

Go语言--复合类型之map、结构体

map Go 语言中的 map(映射、字典)是一种内置的数据结构&#xff0c;它是一个无序的 key-value 对的集合&#xff0c;比如以身份证号作为唯一键来标识一个人的信息。 格式 map [keyType]valueType 在一个 map 里所有的键都是唯一的&#xff0c;而且必须是支持和!操作符的类型…

实验五 图像增强—空域滤波

一、实验目的 了解图像平滑滤波器&#xff08;均值滤波和中值滤波&#xff09;和图像锐化算子&#xff08;Sobel算子、Prewitt算子、Laplacian算子&#xff09;在工程领域中的应用&#xff1b;理解图像平滑滤波器和图像锐化算子的工程应用范围&#xff1b;掌握图像平滑滤波器和…

[终端安全]-4 移动终端之硬件架构安全

1 移动终端硬件架构 上图图展示了典型移动终端硬件架构&#xff0c;包括应用处理器&#xff08;AP&#xff09;、基带处理器&#xff08;BP&#xff09;以及各类共享组件和外设&#xff0c;所有组件通过AXI总线&#xff08;和APB桥&#xff09;连接在一起。以下分别介绍基于整…

【Linux进阶】文件系统6——理解文件操作

目录 1.文件的读取 1.1.目录 1.2.文件 1.3.目录树读取 1.4.文件系统大小与磁盘读取性能 2.增添文件 2.1.数据的不一致&#xff08;Inconsistent&#xff09;状态 2.2.日志式文件系统&#xff08;Journaling filesystem&#xff09; 3.Linux文件系统的运行 4、文件的删…

69.WEB渗透测试-信息收集- WAF、框架组件识别(9)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;68.WEB渗透测试-信息收集- WAF、框架组件识别&#xff08;8&#xff09; 有无waf存在&am…

秋招提前批面试经验分享(下)

⭐️感谢点开文章&#x1f44b;&#xff0c;欢迎来到我的微信公众号&#xff01;我是恒心&#x1f60a; 一位热爱技术分享的博主。如果觉得本文能帮到您&#xff0c;劳烦点个赞、在看支持一下哈&#x1f44d;&#xff01; ⭐️我叫恒心&#xff0c;一名喜欢书写博客的研究生在读…

【MySQL】4.MySQL 的数据类型

MySQL 的数据类型 一.数据类型分类在这里插入图片描述二.注意点1.char VS varchar2.datetime VS timestamp3.enum 和 set 的使用方法 一.数据类型分类 二.注意点 1.char VS varchar char 的意义是直接开辟固定大小的空间&#xff0c;浪费磁盘空间&#xff0c;但是效率高varcha…

解决GPT-4o耗电难题!DeepMind新算法训练效率提升13倍,能耗降低10倍!

目录 01 有更好的解决方案吗&#xff1f; 02 从“超级batch”中筛选数据 03 技术介绍 04 实验结果 生成可学习batch 谷歌DeepMind推出的新算法JEST&#xff0c;将LLM训练的迭代次数减少了13倍&#xff0c;计算量降低了10倍&#xff0c;有望重塑AI未来。GPT-4o早已成为耗能…

下载,连接mysql数据库驱动(最详细)

前言 本篇博客&#xff0c;我讲讲如何连接数据库&#xff1f;我使用mysql数据库举例。 目录 下载对应的数据库jar 包 百度网盘 存有8.4.0版本压缩包&#xff1a;链接&#xff1a;https://pan.baidu.com/s/13uZtXRmuewHRbXaaCU0Xsw?pwduipy 提取码&#xff1a;uipy 复制这…

使用Ubuntu 22.04安装Frappe-Bench【二】

系列文章目录 第一章 使用VMware创建Ubuntu 22.04【一】 文章目录 系列文章目录前言什么是Frappe-Bench&#xff1f;使用安装ERPNext能实现什么效果&#xff1f; 官网给了一个说明 一、使用Ubuntu 22.04安装Frappe-Bench一、安装要求二、安装命令三、 可能出现问题 总结 前言 …

GESP C++一级真题

PDF图片1-7 点赞❤️关注&#x1f60d;收藏⭐️ 互粉必回&#x1f64f;&#x1f64f;&#x1f64f;

对BSV区块链的曼达拉网络通俗易懂的解释

​​发表时间&#xff1a;2023年6月15日 BSV区块链正在引入“曼达拉”升级&#xff0c;使BSV区块链网络的拓扑结构能够适配Teranode&#xff0c;适配这个可以大幅扩容的节点软件。BSV区块链上曼达拉网络的概念并不会改变整个系统的核心规则&#xff1b;相反&#xff0c;它能够引…

基于SSM+JSP的KTV点歌系统(带1w+文档)

基于SSMJSP的KTV点歌系统(带1w文档) 开发一个KTV点歌系统可以解决不利于线下点歌的问题&#xff0c;同时管理员可以利用网络对KTV点歌系统信息进行管理&#xff0c;设计的网站保证信息的完整安全&#xff0c;这样才能提高工作效率&#xff0c;保证系统安全正常的运行。 项目简介…

公众号文章阅读20w+?你猜腾讯给了我多少钱?

前两天写的一篇文章&#xff0c; 《1000T的文件怎么能快速从南京传到北京&#xff1f;最佳方案你肯定想不到》 一不小心被平台推荐&#xff0c;阅读量居然达到了20w&#xff08;这篇收益在文章底部&#xff01;&#xff09;。 留言也是相当精彩 说来惭愧&#xff0c;这篇文章我…

【网络安全】实验三(基于Windows部署CA)

一、配置环境 打开两台虚拟机&#xff0c;并参照下图&#xff0c;搭建网络拓扑环境&#xff0c;要求两台虚拟的IP地址要按照图中的标识进行设置&#xff0c;并根据搭建完成情况&#xff0c;勾选对应选项。注&#xff1a;此处的学号本人学号的最后两位数字&#xff0c;1学号100…

Nestjs基础

一、创建项目 1、创建 安装 Nest CLI&#xff08;只需要安装一次&#xff09; npm i -g nestjs/cli 进入要创建项目的目录&#xff0c;使用 Nest CLI 创建项目 nest new 项目名 运行项目 npm run start 开发环境下运行&#xff0c;自动刷新服务 npm run start:dev 2、…