JUC之锁

乐观锁和悲观锁

悲观锁

当一个线程在操作资源的时候,会悲观的任务有其他的线程会来抢占该资源,因此会在操作资源前进行加锁,避免其他线程抢占。
Synchronized关键字和Lock实现类就是悲观锁。
显示的锁定资源后再对资源进行操作。
使用场景:

  • 适合写操作多的场景。先加锁能够保证写操作时数据正确

本质:
加锁去操作同步资源。

乐观锁

当一个线程去操作资源的时候,会乐观的任务其他线程不会来抢占资源,因此不会加锁。
java中通过无锁编程来实现,只是在对数据进行修改的时候,判断其他线程是否对该数据进行修改过

  • 如果没有修改过,该线程直接修改数据。
  • 如果修改过,该线程则根据不同的实现方式执行不同的操作,比如放弃修改,重试抢锁等等。

原子操作类那些底层的是CAS(Compare And swap)算法,也就是乐观锁。
判断规则:

  • 版本号机制Version(每修改一次版本号递增,当前版本号是最大的,可以直接修改。不是最大的,意味着别人修改过了,我的修改要重新处理)
  • 最常采用的是CAS算法(后面会详细讲,这里略)

使用场景:
乐观锁适合读操作多的场景,不加锁读操作性能大幅提升
本质:
无锁去操作同步资源。

乐观锁和悲观锁举例

乐观锁:Synchronized和Lock的实现类
悲观锁:原子操作的类
在这里插入图片描述

Synchronized

阿里加锁规范

高并发时,同步调用时需要考虑加锁性能损耗。能用无锁数据结构就用无锁数据结构。能用块锁,就不要锁方法体。能用对象锁,就不要用类锁。
(尽可能让锁的代码块尽可能小,避免锁造成不必要的性能开销)

Synchronized三种作用方式

作用于实例方法:当前实例加锁,进入实例前要获取当前实例的锁对象。
作用于代码块:对括号里的对象进行加锁。
作用于静态方法(类方法):对当前类加锁,进去同步代码前要获得当前对象的锁。

Synchronized作用于非静态方法和静态方法的区别(重要)

类中Synchronized修饰非静态方法(对象锁)

  • 加的锁为this对象锁。
  • 一个对象只有一把对象锁,因此多个线程执行一个对象的非静态同步方法时,存在竞争关系。先获得对象锁的线程先执行。(不同对象不会有竞争)
  • 不同对象有不同的对象锁,线程如果持有不同对象锁,线程间无竞争的关系。

类中Synchronized修饰静态方法(类锁)

  • 加的锁为类锁。
  • 先获得类锁的线程先执行。多个线程执行同一个类模板的不同对象的静态同步方法的时候,存在竞争关系。先获得类锁的线程先执行。(同一个对象会竞争,不同对象也会竞争)
  • 不同类有不同的类锁,线程如果持有不同的类锁,线程间无竞争关系
  • 一个对象的类锁和对象锁是不同的锁。一个线程持有类锁,一个线程持有对象锁,线程间无竞争关系。

类中无Syncronize修饰的方法(和锁无关)
线程执行该方法不需要获得锁,直接执行就行了。

代码

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");}public void hello(){System.out.println("-------hello");}
}/*** 题目:谈谈你对多线程锁的理解,8锁案例说明* 口诀:线程   操作  资源类* 8锁案例说明:* 1 标准访问有ab两个线程,请问先打印邮件还是短信* 2 sendEmail方法中加入暂停3秒钟,请问先打印邮件还是短信* 3 添加一个普通的hello方法,请问先打印邮件还是hello* 4 有两部手机,请问先打印邮件还是短信* 5 有两个静态同步方法,有1部手机,请问先打印邮件还是短信* 6 有两个静态同步方法,有2部手机,请问先打印邮件还是短信* 7 有1个静态同步方法,有1个普通同步方法,有1部手机,请问先打印邮件还是短信* 8 有1个静态同步方法,有1个普通同步方法,有2部手机,请问先打印邮件还是短信** 笔记总结:* 1-2(对象锁)*  *  *  一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用其中的一个synchronized方法了,*  *  *  其它的线程都只能等待,换句话说,某一个时刻内,只能有唯一的一个线程去访问这些synchronized方法*  *  *  锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法*  3-4*  *  加个普通方法后发现和同步锁无关*  *  换成两个对象后,不是同一把锁了,情况立刻变化。**  5-6(类锁) 都换成静态同步方法后,情况又变化*  三种 synchronized 锁的内容有一些差别:* 对于普通同步方法,锁的是当前实例对象,通常指this,具体的一部部手机,所有的普通同步方法用的都是同一把锁——>实例对象本身,* 对于静态同步方法,锁的是当前类的Class对象,如Phone.class唯一的一个模板* 对于同步方法块,锁的是 synchronized 括号内的对象** *  7-8*  *    当一个线程试图访问同步代码时它首先必须得到锁,正常退出或抛出异常时必须释放锁。*  *  **  *  *  所有的普通同步方法用的都是同一把锁——实例对象本身,就是new出来的具体实例对象本身,本类this*  *  *  也就是说如果一个实例对象的普通同步方法获取锁后,该实例对象的其他普通同步方法必须等待获取锁的方法释放锁后才能获取锁。*  *  **  *  *  所有的静态同步方法用的也是同一把锁——类对象本身,就是我们说过的唯一模板Class*  *  *  具体实例对象this和唯一模板Class,这两把锁是两个不同的对象,所以静态同步方法与普通同步方法之间是不会有竞态条件的*  *  *  但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁。*/
public class Lock8Demo
{public static void main(String[] args)//一切程序的入口{Phone phone = new Phone();Phone phone2 = new Phone();new Thread(() -> {phone.sendEmail();},"a").start();//暂停毫秒,保证a线程先启动try { TimeUnit.MILLISECONDS.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); }new Thread(() -> {phone.sendSMS();
//            phone.hello();
//            phone2.sendSMS();},"b").start();}
}

字节码角度分析Synchronized

查看反汇编:

javap -c *.java// -c对代码进行反汇编。 -v (verbose)输出附加信息行号,本地变量表,反汇编

synchronized同步代码块

  • 实现使用的是monitorenter和monitorexit。monitorenter代表获得锁对象,monitorexit代表释放锁对象。
  • 通常情况下,一个monitorenter对应两个monitorexit,正常情况下,从第一个monitorexit释放锁。异常情况下,从第二个monitorexit释放锁。

synchronized普通同步方法

调用指令时,先检查ACC_SYNCHRONIZED(Access)标志是否被设置了,如果该方法有这个标志,代表是同步方法,访问的时候要获取锁对象。
方法完成时(无论是否正常介数)释放锁。

synchronized静态同步方法

调用指令时,ACC_STATIC,和ACC_SYNCHRONIZED标志。第一个表示是否静态方法,第二个表示是否同步方法。

反编译Synchronized锁是什么

为什么任何一个对象都可以成为锁?
Java虚拟机支持方法级
什么是管程?
管程(Monitor):可以看做是一个功能模块,他将共享变量和对共享变量的操作封装起来。进程可以调用管程实现进程间的并发控制。
同步指令实现?
Java虚拟机支持方法级的同步方法内部指令序列的同步,这两种同步结构都是由管程(Monitor或者称为锁)来实现的。

  • 方法级的同步:通过读取ACC_SYNCHRONIZED判断是否是同步方法,如果是同步方法,执行线程要求必须持有管程(锁)。执行完毕后释放锁。
  • 方法内部指令序列的同步:同步一段指令序列是通过synchronized方法块来表示。java虚拟机指令集中的monitorenter和monitorexit指令实现的。

Monitor的实现 OjectMonitor

每个对象都关联一个ObjectMonitor锁对象。他有一些属性来保证该资源的同步安全。
在这里插入图片描述
ower: 持有该锁的线程
waitset:存放处于wait状态的线程队列
entrylist:存放等待锁的线程队列
recursions(递归):锁的重入次数
count: 记录该线程获取锁的次数。
在这里插入图片描述

公平锁和非公平锁

公平锁(先来先得)

多个线程按照线程请求锁的先后顺序获取锁。默认都是非公平锁,公平锁需要设置。

Lock lock = new ReentrantLock(true);/l/true表示公平锁,先来先得

执行流程:
获取锁的时候,会将线程自己添加到等待队列中并休眠。当线程使用完锁之后,会去唤醒等待队列首部的线程。线程的休眠和恢复需要从用户态转换为内核态,线程切换是比较慢的,所以公平锁的执行较慢。

非公平锁(随机获得锁,默认)

每个线程获取到锁的顺序是随机的,并不会按照先来先得的顺序。所有的线程会竞争获取锁。
执行流程:
当线程申请锁时,会通过CAS尝试获取锁。如果获取成功,就持有锁对象。如果获取失败,就进入等待队列。好处是不用遵循先到先得的原则,避免了线程的休眠和恢复过程,执行更快。

使用场景

默认是非公平锁。能够让程序执行更快(追求效率)。
非公平锁可能造成线程饿死的情况。

可重入锁(递归锁)

定义

可重入锁又叫递归锁。一个线程在外部方法中获取到锁的时候。在进入内部方法需要获取锁的时候,线程会自动获取到该锁。而不会阻塞。

种类

隐式锁(Synchronized关键字修饰的):

线程在外部获取锁之后,内部自动获取到锁。
实现原理
每个锁对象ObjectMonitor都有一个count计数器ower持有该锁对象的线程。
当执行monitorenter的时候:会看count计数器是否为0,如果为0说明该锁对象没有被其他线程占有,将count计数器+1,将ower设置为当前的线程。如果不为0,该线程需要等待。
当执行monitorexit的时候:会将count计数器减一,count为0代表可以释放。将ower清空。

显式锁(Lock实现类)

 lock.lock();//加锁lock.unlock();//解锁

加锁和释放锁的次数要一样,不然会导致该线程一直持有锁。其他线程无法获取锁。

死锁

一个线程持有某个锁对象,有需要申请其他的锁对象。其他锁对象被另一个线程占有。在无外力干扰的情况下,一直处于僵持状态。
举例: A线程持有obj1锁对象,申请obj2锁对象。B线程持有obj2锁对象,申请obj1锁对象。A,B线程均被阻塞住,处于僵持状态。

手写一个死锁的例子

final Object obj1 = new Object();final Object obj2 = new Object();new Thread(() -> {synchronized (obj1) {System.out.println(Thread.currentThread().getName() + ":" + "拿到了obj1锁对象");System.out.println("等待obj2锁对象...");synchronized (obj2){System.out.println(Thread.currentThread().getName() + ":" + "拿到了obj2锁对象");}}}, "t1").start();new Thread(() -> {synchronized (obj2){System.out.println(Thread.currentThread().getName() + ":" + "拿到了obj2锁对象");System.out.println("等待obj1锁对象...");synchronized (obj1){System.out.println(Thread.currentThread().getName() + ":" + "拿到了obj1锁对象");}}}, "t1").start();

运行结果:
在这里插入图片描述

检测死锁

第一种方式命令行jps+jstack

jps查看死锁线程编号

jps -l

在这里插入图片描述
jstack 查看当前时刻的线程快照

jstack 13992

在这里插入图片描述

第二种jconsole图形化界面

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

生日视频模板-试试这样制作

视频制作已经成为表达情感、记录生活的重要方式。尤其在生日这样的特殊日子,一份个性化的视频祝福不仅能让人感到温馨,还能成为长久珍藏的回忆。那么,如何快速制作出精美的生日模版视频呢?下面就给大家介绍几种可以制作生日模版的…

通信入门系列——离散卷积、连续卷积、卷积性质

本节目录 一、线性系统的激励响应 1、离散δ信号 2、离散卷积 3、连续δ信号 4、连续卷积 二、卷积性质 1、交换律 2、分配律 3、结合律 4、与冲激函数卷积本节内容 一、线性系统的激励响应 输入信号又称为激励,输出信号又称为响应。一个信号输入给一个线性系统的时…

基于单片机设计的智慧农业大棚检测系统

一、设计目标 本项目基于单片机设计一个智慧农业大棚检测系统,以提供实时监测和管理大棚环境的关键参数。系统支持环境温度、湿度检测,光照强度检测,并能根据预设的阀值进行报警提示。为了实现数据的显示和管理,该系统还利用Qt开…

生成式人工智能研究焦点:揭秘基于扩散的模型

生成式人工智能研究焦点:揭秘基于扩散的模型 文章目录 生成式人工智能研究焦点:揭秘基于扩散的模型去噪扩散是什么让扩散发挥作用?采样生成图像的设计选择以更少的步骤理顺流程在低噪音水平下步骤高阶求解器可实现更准确的步骤训练降噪器的设…

50天精通Golang(第17天)

beego框架总结及数据库连接配置 一、beego框架总结 1.1 Beego项目组织架构 上节课程内容对beego的案例代码进行了一个简单的分析,总结一下beego项目的组织结构,总结如下: 1.1.1 项目配置:conf 项目配置文件所在的目录&#x…

异常处理注解 @ExceptionHandler

今天记录下 SpringBoot 中 ExceptionHandler 的使用。 场景 有一个员工表(employee),且给表中的 username 属性设置了唯一性。 -- auto-generated definition create table employee (id bigint auto_increment comment 主键primary key,name va…

【WPF.NET开发】OpenType字体

本文内容 OpenType 字体格式变量大写字母连字花体备用项数字样式版式类 本主题概述了 Windows Presentation Foundation (WPF) 中 OpenType 字体技术的一些主要功能。 1、OpenType 字体格式 OpenType 字体格式是 TrueType 字体格式的扩展,增加了对 PostScript 字…

Linux的SSH远程管理和服务器之间的免密连接

目录 一、远程管理基础 1.ssh协议 2.ssh原理 3、使用ssh协议传输的命令 4.登录方法 二、免密连接 1.免密连接的原理 2.实战 一、远程管理基础 1.ssh协议 ssh协议是基于C/S机构的安全通道协议,通信数据进行加密处理,用于远程管理。 ssh的服务名…

MATLAB二维与三维绘图实验

本文MATLAB源码,下载后直接打开运行即可[点击跳转下载]-附实验报告https://download.csdn.net/download/Coin_Collecter/88740747 一、实验目的 掌握图形对象属性的基本操作。掌握利用图形对象进行绘图操作的方法。 二、实验内容 利用图形对象绘制曲线&#xff…

【图解数据结构】深入剖析时间复杂度与空间复杂度的奥秘

🌈个人主页:聆风吟 🔥系列专栏:图解数据结构、算法模板 🔖少年有梦不应止于心动,更要付诸行动。 文章目录 一. ⛳️算法的定义二. ⛳️算法的特性2.1 🔔输入输出2.2 🔔输入输出2.3 &…

MySQL面试题2

文章目录 面试题 (9-15) 面试题 (9-15) 09)查询学过「张三」老师授课的同学的信息 SELECT s.*,c.cname,t.tname FROM t_mysql_teacher t,t_mysql_student s,t_mysql_course c,t_mysql_score sc WHERE t.tidc.tid and c.cidsc.cid and sc.sids.sid and tname ‘张…

网站建设网络设计营销类网站eyouCMS模板(PC+WAP)

模板介绍: 本模板自带eyoucms内核,无需再下载eyou系统,原创设计、手工书写DIVCSS,完美兼容IE7、Firefox、Chrome、360浏览器等;主流浏览器;结构容易优化;多终端均可正常预览。

【面试合集】说说微信小程序的登录流程?

面试官:说说微信小程序的登录流程? 一、背景 传统的web开发实现登陆功能,一般的做法是输入账号密码、或者输入手机号及短信验证码进行登录 服务端校验用户信息通过之后,下发一个代表登录态的 token 给客户端,以便进行…

线程安全2

文章目录 锁的可重入性死锁内存可见性引起的线程安全 锁的可重入性 直观来看这个代码不能运行 为啥没有出现阻塞? 当前由于是同一个线程,此时的锁对象,就知道了第二次加锁的线程,就是持有锁的线程,第二次操作&#xff…

前端开发必备:掌握正则表达式,轻松应对复杂的表单验证

前言 在前端开发中,经常需要处理 URL 地址、校验手机号合法性、提取域名等。正则表达式是一种常用的工具。通过使用正则表达式,我们可以对用户输入进行有效的验证,确保数据的合法性和完整性。本文将介绍一些常见的正则表达式,帮助…

使用JDK自带的jvisualvm工具查看堆dump文件【回顾】

JDK自带的jvisualvm的使用 打开方式: 直接命令行输入:jvisualvm ,然后回车​​​​​​​ ​​ 或者去jdk的bin目录下找到打开 安装visual GC插件 检测死锁 再点击“死锁 dump”就可以看到死锁的线程信息了;

Vulnhub靶机:driftingblues 4

一、介绍 运行环境:Virtualbox 攻击机:kali(10.0.2.15) 靶机:driftingblues4(10.0.2.20) 目标:获取靶机root权限和flag 靶机下载地址:https://www.vulnhub.com/entr…

【Python数据分析系列】实现txt文件与列表(list)相互读写转换(源码+案例)

这是Python数据分析系列原创文章,我的第199篇原创文章。 一、问题 平时在做数据分析或者程序开发的时候,需要将中间的一些结果或最后的处理结果保存下来,比如保存为txt格式的文本文件,这就涉及列表与txt之间的一种读取和写入操作…

spring cloud feign demo

1. 工程结构 2. 父工程pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.…