【Java难点】多线程终极

悲观锁和乐观锁

悲观锁

synchronized关键字和Lock的实现类都是悲观锁。

它很悲观,认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会一不做二不休的先加锁,确保数据不会被别的线程修改。

适合写操作多的场景,先加锁可以保证写操作时数据正确。

实例:

image-20240428003636339

乐观锁

它很乐观,认为自己在使用数据时不会有别的线程修改数据或资源,所以不会添加锁。

在Java中是通过使用无锁编程来实现,只是在更新数据的时候去判断,之前有没有别的线程更新了这个数据。如果这个数据没有被更新,当前线程将自己修改的数据成功写入。如果这个数据己经被其它线程更新,则根据不同的实现方式执行不同的操作,比如放弃修改、重试抢锁等等。

乐观锁的实现方式:

  • 采用Version版本号机制

  • 采用CAS算法实现

    实例:

image-20240428003707495

锁案例演示

案例1
import java.util.concurrent.TimeUnit;public class JUC04 {public static void main(String[] args) {Phone phone = new Phone();new Thread(()->{phone.sendEmail();},"a").start();new Thread(()->{phone.sendSMS();},"b").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}
}/*** 资源类*/
class Phone{public synchronized void sendEmail(){System.out.println("--------sendEmail");}public synchronized void sendSMS(){System.out.println("--------sendSMS");}
}

请问先打印邮件还是短信?

image-20240428005339903

案例2
import java.util.concurrent.TimeUnit;public class JUC04 {public static void main(String[] args) {Phone phone = new Phone();new Thread(()->{phone.sendEmail();},"a").start();new Thread(()->{phone.sendSMS();},"b").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}
}/*** 资源类*/
class Phone{public synchronized void sendEmail(){try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("--------sendEmail");}public synchronized void sendSMS(){System.out.println("--------sendSMS");}
}

请问先打印邮件还是短信?

image-20240428005646180

案例3
import java.util.concurrent.TimeUnit;public class JUC04 {public static void main(String[] args) {Phone phone = new Phone();new Thread(()->{phone.sendEmail();},"a").start();new Thread(()->{phone.hello();},"b").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}
}/*** 资源类*/
class Phone{public synchronized void sendEmail(){try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("--------sendEmail");}public void hello(){System.out.println("--------hello");}
}

请问先打印邮件还是hello?

image-20240428010005046

案例4
import java.util.concurrent.TimeUnit;public class JUC04 {public static void main(String[] args) {Phone phone = new Phone();Phone phone2 = new Phone();new Thread(()->{phone.sendEmail();},"a").start();new Thread(()->{phone2.sendSMS();},"b").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}
}/*** 资源类*/
class Phone{public synchronized void sendEmail(){try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("--------sendEmail");}public synchronized void sendSMS(){System.out.println("--------sendSMS");}
}

有两部手机,请问先打印邮件还是短信?

image-20240428010211612

案例5
import java.util.concurrent.TimeUnit;public class JUC04 {public static void main(String[] args) {Phone phone = new Phone();new Thread(()->{phone.sendEmail();},"a").start();new Thread(()->{phone.sendSMS();},"b").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}
}/*** 资源类*/
class Phone{public static synchronized void sendEmail(){try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("--------sendEmail");}public static synchronized void sendSMS(){System.out.println("--------sendSMS");}
}

有两个静态同步方法,有一部手机,请问先打印邮件还是短信?

image-20240428010526197

案例6
import java.util.concurrent.TimeUnit;public class JUC04 {public static void main(String[] args) {Phone phone = new Phone();Phone phone2 = new Phone();new Thread(()->{phone.sendEmail();},"a").start();new Thread(()->{phone2.sendSMS();},"b").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}
}/*** 资源类*/
class Phone{public static synchronized void sendEmail(){try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("--------sendEmail");}public static synchronized void sendSMS(){System.out.println("--------sendSMS");}
}

有两个静态同步方法,有两部手机,请问先打印邮件还是短信?

image-20240428010724241

案例7
 import java.util.concurrent.TimeUnit;public class JUC04 {public static void main(String[] args) {Phone phone = new Phone();new Thread(()->{phone.sendEmail();},"a").start();new Thread(()->{phone.sendSMS();},"b").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}
}/*** 资源类*/
class Phone{public static synchronized void sendEmail(){try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}  System.out.println("--------sendEmail");}public static void sendSMS(){System.out.println("--------sendSMS");}
}

有一个静态同步方法,一个普通静态方法,有一部手机,请问先打印邮件还是短信?

image-20240428011114157

案例8
import java.util.concurrent.TimeUnit;public class JUC04 {public static void main(String[] args) {Phone phone = new Phone();Phone phone2 = new Phone();new Thread(()->{phone.sendEmail();},"a").start();new Thread(()->{phone2.sendSMS();},"b").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}
}/*** 资源类*/
class Phone{public static synchronized void sendEmail(){try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("--------sendEmail");}public static void sendSMS(){System.out.println("--------sendSMS");}
}

有一个静态同步方法,一个普通静态方法,有两部手机,请问先打印邮件还是短信?

image-20240428011251980

笔记总结

案例1和案例2

一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用其中的一个synchronized方法了,其它的线程都只能等待,换句话说,某一个时刻内,只能有唯一的一个线程去访问这些synchronized方法,锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的 synchronized方法。

案例3和案例4

普通方法和同步锁无关,换成两个对象后,不是同一把锁了

案例5和案例6

对于普通同步方法,锁的是当前实例对象,通常指this,具体的一部手机,所有的普通同步方法用的都是同一把锁->实例对象本身;

对于静态同步方法,锁的是当前类的class 对象,如Phone.class;

对于同步代码块,锁的是 synchronized 扩号内的对象。

案例7和案例8

当一个线程试图访问同步代码块时,它首先必须得到锁,正常退出或抛出异常时必须释放锁。

所有的普通同步方法用的都是同一把锁-实例对象本身,就是new出来的具体实例对象本身,本类this
也就是说如果一个实例对象的普通同步方法获取锁后,该实例对象的其他普通同步方法必须等待获取锁的方法释放锁后才能再次获取锁。

所有的静态同步方法用的也是同一把锁-类对象(类名.class)本身,具体实例对象this 和类对象本身,这两把锁是两个不同的对象,所以静态同步方法与普通同步方法之间是不会有竞态条件的,但是一但一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁。

synchronized字节码分析

synchronized代码块
public class JUC05 {Object o=new Object();public void m1(){synchronized (o){System.out.println("----hello synchronized code block");}}public static void main(String[] args) {}
}

运行main方法,会在src的同级目录下生成一个target目录,进入target下的classes目录,找到JUC05.class,右键->打开于->终端,在终端中输入javap -c JUC05 ,对 JUC05.class进行反编译,得到如下信息:

image-20240428230628261

synchronized代码块使用的是monitorentermonitorexit指令来持有锁和释放锁。

问: 一定是1个monitorenter对应2个monitorexit吗?

答: 一般情况下,1个monitorenter对应2个monitorexit,但是也存在极端的情况,1个monitorenter对应1个monitorexit

image-20240428230221143

synchronized同步方法

public class JUC05 {public synchronized void m2(){System.out.println("----hello synchronized m2");}public static void main(String[] args) {}
}

运行main方法,会在src的同级目录下生成一个target目录,进入target下的classes目录,找到JUC05.class,右键->打开于->终端,在终端中输入javap -v JUC05 ,对 JUC05.class进行反编译,得到如下信息:

image-20240428231242956

调用指令将会检查方法的ACC_SYNCHRONIZED访问标志是否被设置。如果设置了,执行线程会先持有montor锁, 然后再执行方法,最后在方法完成(无论是正常完成还是非正常完成)时释放monitor

synchronized静态同步方法

public class JUC05 {public synchronized void m2(){System.out.println("----hello synchronized m2");}public static synchronized void m3(){System.out.println("----hello static synchronized m3");}public static void main(String[] args) {}
}

运行main方法,会在src的同级目录下生成一个target目录,进入target下的classes目录,找到JUC05.class,右键->打开于->终端,在终端中输入javap -v JUC05 ,对 JUC05.class进行反编译,得到如下信息:

image-20240428231557432

ACC_STATIC, ACC_SYNCHRONIZED访问标识来区分该方法是否静态同步方法

synchronized底层原语分析

问: 为什么任何一个对象都可以成为一个锁?

答:

公平锁和非公平锁

公平锁

image-20240428235631627

image-20240429002250063

非公平锁

image-20240428235654107

image-20240429002237433

: 为什么会有公平锁和非公平锁的设计?为什么默认非公平?

  1. 恢复挂起的线程到真正锁的获取还是有时间差的,从开发人员来看这个时间微乎其微,但是从CPU的角度来看,这个时间差存在的还是很明显的。所以非公平锁能更充分的利用CPU 的时间片,尽量减少 CPU 空闲状态时间。

  2. 使用多线程很重要的考量点是线程切换的开销,当采用非公平锁时,当1个线程请求锁获取同步状态,然后释放同步状态,所以刚释放锁的线程在此刻再次获取同步状态的概率就变得非常大,所以就减少了线程的开销。

**问:**什么时候用非公平锁,什么时候用公平锁?

:如果为了更高的吞吐量,很显然非公平锁是比较合适的,因为节省很多线程切换时间,吞吐量自然就上去了;否则那就用公平锁,大家公平使用。

可重入锁(递归锁)

定义

可:可以;重:再次;入:进入;锁:同步锁。

image-20240429001200863

重入锁的种类

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【SpringBoot】00 Maven配置及创建项目

一、Maven配置 1、下载Maven 进入官网下载:Maven – Welcome to Apache MavenMaven – Download Apache Maven 本文以最新版为例,可按需选择版本 Maven – Welcome to Apache Maven 2、解压下载好的安装包 将安装包解压到自己设置的空文件夹中 3、…

7.Prism框架之对话框服务

文章目录 一. 目标二. 技能介绍① 什么是Dialog?② Prism中Dialog的实现方式③ Dialog使用案例一 (修改器)④ Dialog使用案例2(异常显示窗口) 一. 目标 1. 什么是Dialog?2. 传统的Dialog如何实现?3. Prism中Dialog实现方式4. 使用Dialog实现一个异常信息弹出框 二. 技能介…

《HCIP-openEuler实验指导手册》1.3Apache动态功能模块加载卸载练习

1.3.1 配置思路 mod_status 模块可以帮助管理员通过web界面监控Apache运行状态,通过LoadModule指令加载该模块,再配置相关权限,并开启ExtendedStatus后,即可使用该模块。 1.3.2 配置步骤 检查mod_status模块状态(使…

C# Solidworks二次开发:访问平面、曲面相关API详解

大家好,今天要介绍的是关于平面、曲面相关的API。 下面是相关的API: (1)第一个为ISurfacePlanarFeatureData,这个API的含义为允许访问平面表面特征,下面是官方的具体解释: 下面是官方使用的例子&#xff…

【网络原理】IP协议的地址管理和路由选择

系列文章目录 【网络通信基础】网络中的常见基本概念 【网络编程】网络编程中的基本概念及Java实现UDP、TCP客户端服务器程序(万字博文) 【网络原理】UDP协议的报文结构 及 校验和字段的错误检测机制(CRC算法、MD5算法) 【网络…

【网络原理】TCP协议的连接管理机制(三次握手和四次挥手)

系列文章目录 【网络通信基础】网络中的常见基本概念 【网络编程】网络编程中的基本概念及Java实现UDP、TCP客户端服务器程序(万字博文) 【网络原理】UDP协议的报文结构 及 校验和字段的错误检测机制(CRC算法、MD5算法) 【网络…

扭蛋机小程序带来了什么优势?扭蛋机收益攻略

在当下的潮流消费时代,人们对潮玩也日益个性化,扭蛋机作为一种新型的娱乐消费模式,深受大众喜爱。扭蛋机的价格低,各个年龄层的玩家都可以进行购买,潜在玩家量非常大。扭蛋机商品主打热门IP周边等,种类繁多…

大型零售企业,适合什么样的企业邮箱大文件解决方案?

大型零售企业通常指的是在全球或特定地区内具有显著市场影响力和知名度的零售商。这些企业不仅在零售业务收入上达到了惊人的规模,而且在全球范围内拥有广泛的销售网络和实体店铺。它们在快速变化的零售行业中持续创新,通过实体店、电商平台等多种渠道吸…

第十一章 Spring Boot 整合 WebSocket

第十一章 Spring Boot 整合 WebSocket 1. 为什么需要 WebSocket2. WebSocket 简介3. Spring Boot 整合 WebSocket3.1 实现消息群发1. 依赖2. 配置 WebSocket ************************************************************ 1. 为什么需要 WebSocket 2. WebSocket 简介 3. Spri…

QT支持多种开发语言

QT主要是一个C应用程序框架,但它也提供了对其他一些编程语言的官方或非官方支持。以下是QT支持的一些语言版本及其特点。北京木奇移动技术有限公司,专业的软件外包开发公司,欢迎交流合作。 1.Python (PyQt) : PyQt是QT的官方Pyth…

axios.get请求 重复键问题??

封装的接口方法: 数据: 多选框多选后 能得到对应的数组 但是请求的载荷却是这样的,导致会请求不到数据 departmentChecks 的格式看起来是一个数组,但是通常 HTTP 请求的查询参数不支持使用相同的键(key)名多次。如…

【Redis 开发】Redis哨兵

哨兵 作用和原理服务状态监控选举新的master 搭建哨兵集群RedisTemplate的哨兵模式 作用和原理 Redis提供了哨兵机制来实现主从集群中的自动故障恢复: 哨兵也是一个集群 监控:会不断检查master和slave是否按预期工作自动故障恢复:如果mast…

本地生活服务平台有哪些?哪个靠谱?

随着多家互联网大厂的本地生活服务布局日益展开,不少人都看到了其中的巨大市场缺口和广阔前景,想要入驻本地生活服务平台,瓜分这块巨大的蛋糕。而在当下这个选择大于努力的时代,能否分到蛋糕以及分到多少蛋糕的关键,就…

Vast+产品展厅 | Vastbase G100数据库是什么架构?(2)

Vastbase G100是海量数据融合了多年对各行业应用场景的深入理解,基于openGauss内核开发的企业级关系型数据库。 上一期,《Vast产品展厅》为您介绍了Vastbase G100的部署架构和物理架构。 本期,我们将为您详细讲解Vastbase G100的物理架构和…

基于Python实现心脏病数据可视化DEA+预测【500010103.1】

一、数据说明 该心脏病数据集是通过组合 5 个已经独立可用但以前未合并的流行心脏病数据集来策划的。在这个数据集中,5 个心脏数据集结合了 11 个共同特征,使其成为迄今为止可用于研究目的的最大心脏病数据集。 该数据集由 1190 个实例和 11 个特征组成…

PVE虚拟机隐藏状态栏虚拟设备

虚拟机启动后,状态栏会出现一些虚拟设备,点击弹出会导致虚拟机无法使用。 解决方案: 1、在桌面新建disable_virtio_removale.bat文件,内容如下: ECHO OFF FOR /f %%A IN (reg query "HKLM\SYSTEM\CurrentContro…

低代码+定制物资管理:创新解决方案探析

引言 在当今快速变化的商业环境中,企业面临着不断增长的挑战,如提高效率、降低成本、满足客户需求等。为了应对这些挑战,企业需要不断创新并采用先进的技术解决方案。在这样的背景下,低代码开发和定制化物资管理成为了引领企业变…

Spark-机器学习(7)分类学习之决策树

在之前的文章中,我们学习了分类学习之支持向量机,并带来简单案例,学习用法。想了解的朋友可以查看这篇文章。同时,希望我的文章能帮助到你,如果觉得我的文章写的不错,请留下你宝贵的点赞,谢谢。…

selenium在Pycharm中结合python的基本使用、交互、无界面访问

下载 下载与浏览器匹配的浏览器驱动文件,这里一定注意的是,要选择和浏览器版本号相同的驱动程序,否则后面会有很多问题。 (1)浏览器(以google为例)版本号的查询: 我这里的版本号是1…

MySQL数据库安装——zip压缩包形式

安装压缩包zip形式的 MySQL 8数据库 一 、先进入官网下载 https://dev.mysql.com/downloads/mysql/ 二、解压到某个文件夹 我解压到了D:\mysql\mysql8 下面 然后在这个文件夹下手动创建 my.ini 文件和 data 文件夹 my.ini 内容如下: 注意 basedir 和 datadi…