Java多线程锁

多线程锁

本专栏学习内容又是来自尚硅谷周阳老师的视频

有兴趣的小伙伴可以点击视频地址观看

Synchronized

Synchronized是Java中锁的一种实现方法,我们需要了解他锁在什么地方,锁的类型有哪些

阿里巴巴开发手册规定:

高并发时,同步调用应该去考量锁的性能消耗,能用无锁数据结构,就不要用锁;能锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁

说明:尽可能使加锁的代码块工作量尽可能的小,避免在锁代码块中调用RPC方法

同步方法

操控两个线程、一个资源类

资源类

class Phone{public synchronized void sendEmail(){try {TimeUnit.MILLISECONDS.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("----sendEmail");}public synchronized void sendSMS(){System.out.println("----sendSMS");}public void hello(){System.out.println("hello");}
}

一个资源对象执行两个同步方法

线程A执行sendEmail()时会加锁,锁的对象是new Phone()也就是堆空间中的那个对象,在线程B调用sendSMS()时,锁对象也是new Phone(),所以需要等待线程A执行完毕才能获取锁

public class SyncDemo1 {public static void main(String[] args) throws InterruptedException {Phone phone = new Phone();new Thread(() -> {phone.sendEmail();},"a").start();//保证线程a先运行TimeUnit.MILLISECONDS.sleep(200);new Thread(() -> {phone.sendSMS();},"b").start();}
}//结果
----sendEmail
----sendSMS

一个资源对象执行一个同步方法和一个普通方法

这个就比较简单,因为hello()不需要获取锁,可以直接执行

public static void main(String[] args) throws InterruptedException {Phone phone = new Phone();new Thread(() -> {phone.sendEmail();},"a").start();//保证线程a先运行TimeUnit.MILLISECONDS.sleep(200);new Thread(() -> {phone.hello();},"b").start();
}//结果
hello
----sendEmail

两个资源对象执行两个同步方法

因为这两个方法的锁对象不同,所以互不影响

public static void main(String[] args) throws InterruptedException {Phone phone = new Phone();Phone phone2 = new Phone();new Thread(() -> {phone.sendEmail();},"a").start();//保证线程a先运行TimeUnit.MILLISECONDS.sleep(200);new Thread(() -> {phone2.sendSMS();},"b").start();
}//结果
----sendSMS
----sendEmail

静态同步方法

资源类

class Phone{public static synchronized void sendEmail(){try {TimeUnit.MILLISECONDS.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("----sendEmail");}public static synchronized void sendSMS(){System.out.println("----sendSMS");}public void hello(){System.out.println("hello");}
}

一个资源对象执行两个静态同步方法

对于静态同步方法,锁住的是Phone这个Class对象,也就是存在与方法区中的Phone,所以不管是一个资源对象还是多个资源对象,调用静态同步方法,使用的都是同一个锁

public static void main(String[] args) throws InterruptedException {Phone phone = new Phone();new Thread(() -> {phone.sendEmail();},"a").start();//保证线程a先运行TimeUnit.MILLISECONDS.sleep(200);new Thread(() -> {phone.sendSMS();},"b").start();
}//结果
----sendEmail
----sendSMS

同步代码块

同步代码块的锁,就是括号中填的对象,可以是对象锁,也可以是类锁

synchronized (this) {}

字节码角度分析

使用javap -c xxxx.class可以反编译字节码文件,如果要看详细信息可以使用javap -v xxxx.class

同步代码块

public class SyncDemo2 {Object object = new Object();public void m1() {synchronized (object) {System.out.println("m1 method");}}public static void main(String[] args) throws InterruptedException {}
}

在JVM中是由monitore来控制锁的,但是在同步代码块中,发现有一个获取锁,有两个释放锁

第二个释放锁有点保护机制的意思,如果同步代码块中出现异常,无法正常释放锁,会有异常的释放方式

image-20230725162440971

同步方法和静态同步方法

    public synchronized void m2() {System.out.println("m2 method");}public static synchronized void m3() {System.out.println("m2 method");}

JVM中使用ACC_SYNCHRONIZED来表示当前方法是同步方法,使用ACC_STATIC来表示该方法为静态方法

image-20230725162921402

公平锁、非公平锁

非公平锁

非公平锁是一种线程同步机制,它允许新的线程在获取锁时,不考虑其他等待线程的顺序,有可能插队获取到锁资源。相对于公平锁来说,非公平锁在一定程度上可以提高系统的吞吐量,但可能导致某些线程长时间地等待。

模拟卖票案例

一共50张票,交给a、b、c三个窗口去卖

public class LockDemo1 {public static void main(String[] args) {Ticket ticket = new Ticket();new Thread(() -> {for(int i = 0;i < 60;i++) ticket.buy();},"a").start();new Thread(() -> {for(int i = 0;i < 60;i++) ticket.buy();},"b").start();new Thread(() -> {for(int i = 0;i < 60;i++) ticket.buy();},"c").start();}
}class Ticket {private int sum = 50;//默认使用非公平锁private ReentrantLock lock = new ReentrantLock();public void buy() {try {lock.lock();if (sum > 0) {System.out.println(Thread.currentThread().getName() + "卖出第  " + sum + " 张票,还剩 " + --sum);}} finally {lock.unlock();}}
}

通过观察结果可以发现,可能有一个窗口把50张票卖完,也有可能一个窗口一张票都卖不出

image-20230726140159232

公平锁

公平锁是一种线程同步机制,它按照线程请求锁的顺序来分配锁资源,保证线程获取锁的顺序与其请求锁的顺序一致。公平锁可以避免线程饥饿的情况,但可能降低系统的吞吐量。

模拟买票案例

可以使用new ReentrantLock(true)来创建公平锁,可以从结果看出,运行一段时间后会保证顺序获取锁

image-20230726140425728

如何选择

一般来说,对于线程执行顺序要求不高的,完全可以使用非公平锁,因为线程之间的切换是非常消耗时间的,非公平锁可以提高吞吐量。

可重入锁

简单理解为:可以重复进入的同步锁,当然是有前提条件的

概念

可重入锁是一种线程同步机制,也称为递归锁。它允许同一个线程在拥有锁的情况下多次进入被锁定的代码块,而不会造成死锁。可重入锁在保证线程安全的同时,提供了更大的灵活性和方便性。

代码演示

synchronizedReentrantLock都属于可重入锁

synchronized

如果不是可重入锁,按照同步锁的理论知识,外层获取object锁时,第二层应该就不能获取到该锁,程序应该会卡死在那里,但是我们发现程序正常的执行完毕,由此可见synchronized是可重入锁

public static void main(String[] args) throws InterruptedException {final Object object = new Object();synchronized (object) {System.out.println(Thread.currentThread().getName() + "进入外层");synchronized (object) {System.out.println(Thread.currentThread().getName() + "进入中层");synchronized (object) {System.out.println(Thread.currentThread().getName() + "进入内层");}}}
}//结果
main进入外层
main进入中层
main进入内层

ReentrantLock

ReentrantLock锁对象是ReentrantLock类的实例,同样也是可重入锁

public static void main(String[] args) {ReentrantLock lock = new ReentrantLock();lock.lock();try {System.out.println(Thread.currentThread().getName() + "进入外层");lock.lock();try {System.out.println(Thread.currentThread().getName() + "进入中层");lock.lock();try {System.out.println(Thread.currentThread().getName() + "进入内层");}finally {lock.unlock();}}finally {lock.unlock();}}finally {lock.unlock();}
}//结果
main进入外层
main进入中层
main进入内层

原理

每一个锁对象拥有一个锁计数器和一个指向持有该锁的线程的指针

当执行monitorenter时,如果锁对象的计数器为0,那么说明他没有被其他线程所持有,JVM会将锁对象的持有线程设置为当前线程,并且将其计数器+1。

在目标锁的计数器不为0的情况下,如果锁对象的持有线程是当前线程,那么JVM可以将其计数器+1,否则需要等待。

当执行monitorexit时,JVM会将对象的计数器-1,计数器为0代表锁已经被释放。

死锁

死锁是多线程编程中一种常见的情况,指的是两个或多个线程无限期地等待对方释放资源,从而导致程序无法继续执行的状态。

public class SycnDemo04 {static Object a = new Object();static Object b = new Object();public static void main(String[] args) {new Thread(()->{synchronized (a){System.out.println(Thread.currentThread().getName() + "获取了锁a");try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}synchronized (b){System.out.println(Thread.currentThread().getName() + "获取了锁b");}}},"A").start();new Thread(()->{synchronized (b){System.out.println(Thread.currentThread().getName() + "获取了锁b");try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}synchronized (a){System.out.println(Thread.currentThread().getName() + "获取了锁a");}}},"B").start();}
}//结果
A获取了锁a
B获取了锁b

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

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

相关文章

实训笔记7.26

实训笔记7.26 7.26笔记一、Hadoop大数据开发技术1.1 Hadoop的安装部署1.2 Hadoop的分布式文件存储系统HDFS1.2.1 HDFS的组成1.2.2 HDFS的操作方式1.2.3 HDFS的流程原理1.2.4 HDFS核心概念 1.3 Hadoop的分布式资源调度系统YARN1.3.1 YARN的组成1.3.2 YARN的工作流程1.3.3 YARN的…

将Spring Session存储到Redis中实现持久化

文章目录 Session持久化1. 添加依赖2. 配置redis连接信息3. 存储和读取session从Redis Session持久化 1. 添加依赖 在项目中添加session依赖和redis依赖&#xff0c;如下所示&#xff1a; <dependency><groupId>org.springframework.boot</groupId><art…

15个图像识别模型下载及优缺点分析

1、PixelCNN & PixelRNN in TensorFlow TensorFlow implementation of Pixel Recurrent Neural Networks. 地址&#xff1a;https://github.com/carpedm20/pixel-rnn-tensorflow 优点&#xff1a;这些模型是生成图像的有力工具&#xff0c;可以生成高质量的逼真图像。它们…

Pytorch个人学习记录总结 04

目录 torchvision DataLoader torchvision transforams是对单张图片进行处理&#xff0c;而制作数据集的时候&#xff0c;是需要对图像进行批量处理的。因此本节是将torchvision中的datasets和transforms联合使用对数据集进行预处理操作。 &#xff08;torchvision官方文档地…

基于VUE3+Layui从头搭建通用后台管理系统(前端篇)五:后台主页功能实现上

一、本章内容 本章实现后台主页框架实现、菜单的动态加载及生产、tab组件与菜单绑定、菜单与路由绑定等,工具栏按钮等界面及对应功能实现。 1. 详细课程地址: 待发布 2. 源码下载地址: 待发布 二、界面预览

Stream 流式编程不常用API讲解

常用的 Stream 操作方法 FlatMap 扁平映射&#xff08;FlatMap&#xff09;&#xff1a;flatMap() 方法类似于 map() 方法&#xff0c;不同之处在于它可以将每个元素映射为一个流&#xff0c;并将所有流连接成一个流。这主要用于解决嵌套集合的情况。例如&#xff1a; List&…

vue脚手架文件说明

vue脚手架文件说明 1、文件介绍2、脚手架里面主要文件和作用 1、文件介绍 2、脚手架里面主要文件和作用 node_modules 都是下载的第三方包public/index.html 浏览器运行的网页src/main.js webpack打包的入口src/APP.vue Vue页面入口package.json 依赖包列表文件

java篇 类的进阶0x07:继承

文章目录 继承继承的语法继承的作用与特点继承与组合的区别 覆盖 overideOverride构造方法无法覆盖 super&#xff1a;和父类对象沟通的桥梁super 到底指哪个父类的实例super 严格意义上并非真的是一个父类的引用super 调用父类的构造方法子类构造方法被调用时&#xff0c;Java…

为什么 SSH(安全终端)的端口号是 22 !!

导读为什么 SSH&#xff08;安全终端&#xff09;的端口号是 22 呢&#xff0c;这不是一个巧合&#xff0c;这其中有个我&#xff08;Tatu Ylonen&#xff0c;SSH 协议的设计者&#xff09;未曾诉说的故事。 将 SSH 协议端口号设为 22 的故事 1995 年春我编写了 SSH 协议的最…

E2E工程问题:小周期转大周期Gateway

摘要&#xff1a; 本文讨论一个具体的工程问题&#xff0c;E2E报文对应的信号&#xff0c;由小周期转大周期导致的E2E校验失败问题。 工程中&#xff0c;网关节点很重要的一个功能就是路由。当然&#xff0c;E2E&#xff08;End to End&#xff09;报文也可路由&#xff0c;但…

ChatGPT在智能推送和个性化广告中的应用如何?

ChatGPT在智能推送和个性化广告领域具有广泛的应用潜力。智能推送和个性化广告是指根据用户的个性化需求和兴趣&#xff0c;精准地向用户推送相关的信息和广告内容。ChatGPT作为一种预训练的通用语言模型&#xff0c;具有强大的语言理解和生成能力&#xff0c;可以在智能推送和…

【spring boot】spring boot下代码运行逻辑

概括 springboot是一种java开发框架&#xff0c;采用注解开发形式&#xff0c;大大简化了SSM框架下的大量配置&#xff0c; 目前springboot想要实现一个功能&#xff0c;一般通用方式是建立这几个包&#xff1a;controller&#xff0c;config&#xff0c;mapper&#xff0c;e…

5分钟开发一个AI论文抓取和ChatGPT提炼应用

5分钟开发一个AI论文抓取和ChatGPT提炼应用 第一步 点击“即刻开始” -选择模板 python -修改标题 “AIPaper”&#xff0c;项目标识“AIPaper”&#xff0c;点击“创建项目” 第二步 在编程区域右侧AI区域&#xff0c;输入框输入以下内容&#xff1a; 请根据下面的内容&…

Java后端程序员不得不知道的 API 接口常识

说实话&#xff0c;我非常希望自己能早点看到本篇文章&#xff0c;大学那个时候懵懵懂懂&#xff0c;跟着网上的免费教程做了一个购物商城就屁颠屁颠往简历上写。 至今我仍清晰地记得&#xff0c;那个电商教程是怎么定义接口的&#xff1a; 管它是增加、修改、删除、带参查询&…

ELK 使用kibana查询和分析nginx日志

背景&#xff1a;使用kibana查询和分析nginx请求日志&#xff0c;方便开发人员查询系统日志和分析系统问题。 setp 1、定义Index patterns 2、定义Discover(Search 查询数据) 3、定义Visualizations 3.1 定义Vertical Bar 3.2 、Choose a source 3.3、定义图表 4、定义…

【一文搞懂】—带霍尔编码器的直流有刷减速电机

文章目录 一、直流有刷电机二、减速比三、霍尔编码器3.1 霍尔编码器3.2 霍尔编码器测速原理 四、测速程序设计4.1 跳变沿检测4.2 计算转速 一、直流有刷电机 宏观上说直流有刷电机由固定部分&#xff08;定子&#xff09;和旋转部分&#xff08;转子&#xff09;组成。在定子上…

同一份数据,Redis为什么要存两次

Redis作为目前最主流的高性能缓存&#xff0c;里面有很多精妙的设计&#xff0c;其中有一种数据类型&#xff0c;当在存储的时候会同时采用两种数据结构来进行分别存储&#xff0c;那么 Redis 为什么要这么做呢&#xff1f;这么做会造成同一份数据占用两倍空间吗&#xff1f; …

Reinforcement Learning with Code 【Chapter 7. Temporal-Difference Learning】

Reinforcement Learning with Code This note records how the author begin to learn RL. Both theoretical understanding and code practice are presented. Many material are referenced such as ZhaoShiyu’s Mathematical Foundation of Reinforcement Learning, . 文章…

高层金属做power mesh如何避免via stack

随着工艺精进&#xff0c;pr要处理的层次也越来越多&#xff0c;如何选择power plan的层次尤为关键&#xff0c;一方面决定ir drop的大小&#xff0c;影响着芯片的功能&#xff0c;一方面决定绕线资源&#xff0c;影响面积。 选择高层metal做power mesh的关键在于厚金属&#…

局域网内主机ping不通,但是可以调用对方http接口(防火墙阻止了icmp协议)(关闭防火墙或者启用ICMP回显请求(ICMPv4-In))

文章目录 背景可能的原因问题排查及解决 背景 局域网内有一台主机&#xff0c;ping它ping不通&#xff0c;但是可以调用它的http接口&#xff0c;很诡异。。。 可能的原因 可能的原因有以下几种&#xff1a; 防火墙设置&#xff1a;局域网内的主机可能设置了防火墙&#xff…