Java进阶(锁)——锁的升级,synchronized与lock锁区别

目录

  • 引出
    • Java中锁升级
    • synchronized与lock锁区别
  • 缓存三兄弟:缓存击穿、穿透、雪崩
    • 缓存击穿
    • 缓存穿透
    • 缓存雪崩
  • 总结

引出

Java进阶(锁)——锁的升级,synchronized与lock锁区别


Java中锁升级

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

看一段代码:

public class App {public static void main(String[] args) throws InterruptedException {Calculate cal = new Calculate();long start = System.currentTimeMillis();Thread t1 = new Thread(()->{for (int i = 0; i < 1000_0000; i++) {cal.increase();}});Thread t2 = new Thread(()->{for (int i = 0; i < 1000_0000; i++) {cal.increase();}});t1.start();t2.start();t1.join();t2.join();System.out.println("time = " + (System.currentTimeMillis()-start));System.out.println("cal.getNum() = " + cal.getNum());}
}
public class Calculate {private int num;public int getNum() {return num;}// 多线程执行:public void increase() 会有线程安全问题// synchronized 锁解决,锁的是什么?public void increase(){synchronized (this) {num++;}}
}

分析:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

对象组成:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Mark Word用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等,占用内存大小与虚拟机位长一致。Mark Word对应的类型是markOop。源码位于markOop.hpp中。在64位虚拟机下,Mark Word是64bit大小的,其存储结构如下:

对象头在64位虚拟机 8个字节

在这里插入图片描述

在这里插入图片描述

package cn.wn.juc;
import org.openjdk.jol.info.ClassLayout;
import java.util.concurrent.TimeUnit;
public class App {// 锁升级演示public static void main(String[] args) throws InterruptedException {User user01 = new User();System.out.println("无状态(001):" + ClassLayout.parseInstance(user01).toPrintable());// 从jdk6开始,jvm默认延迟4s自动开启开启偏向锁。通过-XX:BiasedLockingStartupDelay=0设置取消延迟// 如果不要偏向锁:-XX:-UseBiasedLocking=falseTimeUnit.SECONDS.sleep(5);User user02 = new User();System.out.println("启用偏向锁(101):" + ClassLayout.parseInstance(user02).toPrintable());for (int i = 0; i < 2; i++) {synchronized (user02) {System.out.println("偏向锁(101)带线程ID:" + ClassLayout.parseInstance(user02).toPrintable());}// 偏向锁释放,对象头不会变化,一直存在, (偏向线程id) 下次执行判断是否同一个线程如果是直接执行System.out.println("偏向锁(101)释放线程ID:" + ClassLayout.parseInstance(user02).toPrintable());}// 多个线程加锁,升级为轻量级锁new Thread(() -> {synchronized (user02) {System.out.println("轻量级锁(00):" + ClassLayout.parseInstance(user02).toPrintable());try {System.out.println("=====休眠3秒======");TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("轻量-->重量(10):" + ClassLayout.parseInstance(user02).toPrintable());}}).start();TimeUnit.SECONDS.sleep(1);new Thread(() -> {synchronized (user02) {System.out.println("重量级锁(10):" + ClassLayout.parseInstance(user02).toPrintable());}}).start();}
}
<dependency><groupId>org.openjdk.jol</groupId><artifactId>jol-core</artifactId><version>0.10</version>
</dependency>

锁的状态总共有四种,级别由低到高依次为:无锁、偏向锁、轻量级锁、重量级锁,这四种锁状态分别代表什么,为什么会有锁升级?其实在 JDK 1.6之前,synchronized 还是一个重量级锁,是一个效率比较低下的锁,但是在JDK 1.6后,Jvm为了提高锁的获取与释放效率对(synchronized )进行了优化,引入了 偏向锁 和 轻量级锁 ,从此以后锁的状态就有了四种(无锁、偏向锁、轻量级锁、重量级锁),并且四种状态会随着竞争的情况逐渐升级,而且是不可逆的过程,即不可降级,也就是说只能进行锁升级(从低级别到高级别),不能锁降级(高级别到低级别),意味着偏向锁升级成轻量级锁后不能降级成偏向锁。这种锁升级却不能降级的策略,目的是为了提高获得锁和释放锁的效率。

无锁

无锁是指没有对资源进行锁定,所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功。

无锁的特点是修改操作会在循环内进行,线程会不断的尝试修改共享资源。如果没有冲突就修改成功并退出,否则就会继续循环尝试。如果有多个线程修改同一个值,必定会有一个线程能修改成功,而其他修改失败的线程会不断重试直到修改成功。

偏向锁

初次执行到synchronized代码块的时候,锁对象变成偏向锁(通过CAS修改对象头里的锁标志位),字面意思是“偏向于第一个获得它的线程”的锁。执行完同步代码块后,线程并不会主动释放偏向锁。当第二次到达同步代码块时,线程会判断此时持有锁的线程是否就是自己(持有锁的线程ID也在对象头里),如果是则正常往下执行。由于之前没有释放锁,这里也就不需要重新加锁。如果自始至终使用锁的线程只有一个,很明显偏向锁几乎没有额外开销,性能极高。

偏向锁是指当一段同步代码一直被同一个线程所访问时,即不存在多个线程的竞争时,那么该线程在后续访问时便会自动获得锁,从而降低获取锁带来的消耗,即提高性能。

当一个线程访问同步代码块并获取锁时,会在 Mark Word 里存储锁偏向的线程 ID。在线程进入和退出同步块时不再通过 CAS 操作来加锁和解锁,而是检测 Mark Word 里是否存储着指向当前线程的偏向锁。轻量级锁的获取及释放依赖多次 CAS 原子指令,而偏向锁只需要在置换 ThreadID 的时候依赖一次 CAS 原子指令即可。

偏向锁只有遇到其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁,线程是不会主动释放偏向锁的。

轻量级锁

轻量级锁是指当锁是偏向锁的时候,却被另外的线程所访问,此时偏向锁就会升级为轻量级锁,其他线程会通过自旋(关于自旋的介绍见文末)的形式尝试获取锁,线程不会阻塞,从而提高性能。

一旦有第二个线程加入锁竞争,偏向锁就升级为轻量级锁(自旋锁)。这里要明确一下什么是锁竞争:如果多个线程轮流获取一个锁,但是每次获取锁的时候都很顺利,没有发生阻塞,那么就不存在锁竞争。只有当某线程尝试获取锁的时候,发现该锁已经被占用,只能等待其释放,这才发生了锁竞争。

在轻量级锁状态下继续锁竞争,没有抢到锁的线程将自旋,即不停地循环判断锁是否能够被成功获取。获取锁的操作,其实就是通过CAS修改对象头里的锁标志位。先比较当前锁标志位是否为“释放”,如果是则将其设置为“锁定”,比较并设置是原子性发生的。这就算抢到锁了,然后线程将当前锁的持有者信息修改为自己。

长时间的自旋操作是非常消耗资源的,一个线程持有锁,其他线程就只能在原地空耗CPU,执行不了任何有效的任务,这种现象叫做忙等(busy-waiting)。如果多个线程用一个锁,但是没有发生锁竞争,或者发生了很轻微的锁竞争,那么synchronized就用轻量级锁,允许短时间的忙等现象。这是一种折衷的想法,短时间的忙等,换取线程在用户态和内核态之间切换的开销。

重量级锁

重量级锁显然,此忙等是有限度的(有个计数器记录自旋次数,默认允许循环10次,可以通过虚拟机参数更改)。如果锁竞争情况严重,某个达到最大自旋次数的线程,会将轻量级锁升级为重量级锁(依然是CAS修改锁标志位,但不修改持有锁的线程ID)。当后续线程尝试获取锁时,发现被占用的锁是重量级锁,则直接将自己挂起(而不是忙等),等待将来被唤醒。

重量级锁是指当有一个线程获取锁之后,其余所有等待获取该锁的线程都会处于阻塞状态。

简言之,就是所有的控制权都交给了操作系统,由操作系统来负责线程间的调度和线程的状态变更。而这样会出现频繁地对线程运行状态的切换,线程的挂起和唤醒,从而消耗大量的系统资

synchronized与lock锁区别

总结如下:

1️⃣ lock 是一个接口,而 synchronized 是 Java 的一个关键字,synchronized 是内置的语言实现。
2️⃣ 异常是否释放锁:synchronized 在发生异常时候会自动释放占有的锁,因此不会出现死锁;而 lock 发生异常时候,不会主动释放占有的锁,必须手动 unlock 来释放锁,可能引起死锁的发生。(所以最好将同步代码块用 try catch 包起来,finally 中写入 unlock,避免死锁的发生。)
3️⃣ 是否响应中断
lock 等待锁过程中可以用 interrupt 来中断等待,而 synchronized 只能等待锁的释放,不能响应中断。
4️⃣ 是否知道获取锁:Lock 可以通过 trylock 来知道有没有获取锁,而 synchronized 不能。
5️⃣ Lock 可以提高多个线程进行读操作的效率。(可以通过 ReadWriteLock 实现读写分离)
6️⃣ 在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时 Lock 的性能要远远优于 synchronized。所以说,在具体使用时要根据适当情况选择。
7️⃣ synchronized 使用 Object 对象本身的 wait 、notify、notifyAll 调度机制,而 Lock 可以使用 Condition 进行线程之间的调度。

性能区别

synchronized 和 lock 性能区别
synchronized 是托管给 JVM 执行的,而 lock 是 Java 写的控制锁的代码。在 Java1.5 中,synchronized 是性能低效的。因为这是一个重量级操作,但是到了 Java1.6,发生了变化,进行了很多优化,有适应自旋,轻量级锁,偏向锁等等。
synchronized 原始采用的是 CPU悲观锁机制,即线程获得的是排他锁。排他锁意味着其他线程只能依靠阻塞来等待线程释放锁。
而 Lock 用的是乐观锁机制。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。乐观锁实现的机制就是 CAS 操作(Compare and Swap)。进一步研究 ReentrantLock 的源码,会发现其中比较重要的获得锁的一个方法是 compareAndSetState。这里其实就是调用的 CPU 提供的特殊指令。

用途区别

synchronized 和 lock 用途区别
synchronized 原语和 ReentrantLock 在一般情况下没有什么区别,但是在非常复杂的同步应用中,请考虑使用 ReentrantLock ,特别是遇到下面两种需求的时候。
1️⃣ 某个线程在等待一个锁的控制权的这段时间需要中断。
2️⃣ 分开处理一些 wait-notify,ReentrantLock 里面的 Condition 应用,能够控制 notify 哪个线程。
3️⃣ 公平锁功能,每个到来的线程都将排队等候。

先说第一种情况,ReentrantLock 的 lock 机制有 2 种,忽略中断锁和响应中断锁,这带来了很大的灵活性。比如:如果 A、B 两个线程去竞争锁,A 线程得到了锁,B 线程等待,但是 A 线程这个时候实在有太多事情要处理,就是一直不返回,B 线程可能就会等不及了,想中断自己,不再等待这个锁了,转而处理其他事情。

这个时候 ReentrantLock 就提供了两种机制:可中断/可不中断:
①B 线程中断自己(或者别的线程中断它),但是 ReentrantLock 不去响应,继续让 B 线程等待,你再怎么中断,我全当耳边风(synchronized 原语就是如此);
②B 线程中断自己(或者别的线程中断它),ReentrantLock 处理了这个中断,并且不再等待这个锁的到来,完全放弃。

缓存三兄弟:缓存击穿、穿透、雪崩

缓存击穿

缓存击穿:redis中没有,但是数据库有

顺序:先查缓存,判断缓存是否存在;如果缓存存在,直接返回数据;如果缓存不存在,則查询数据库,将数据库的数据存入到缓存

在这里插入图片描述

解决方案:将热点数据设置过期时间长一点;针对数据库的热点访问方法上分布式锁;

缓存穿透

缓存穿透:redis中没有,数据库也没有

在这里插入图片描述

解决方案:

(1)将不存在的key,在redis设置值为null;

(2)使用布隆过滤器;

原理:https://zhuanlan.zhihu.com/p/616911933

在这里插入图片描述

布隆过滤器:

如果确认key不存在于redis中,那么就一定不存在;

它说key存在,就有可能存在,也可能不存在! (误差)

在这里插入图片描述

布隆过滤器

1、根据配置类中的 key的数量 ,误差率,计算位图数组【二维数组】

2、通过布隆过滤器存放key的时候,会计算出需要多少个hash函数,由hash函数算出多少个位图位置需要设定为1

3、查询时,根据对应的hash函数,判断对应的位置值是否都为1;如果有位置为0,则表示key一定不存在于该redis服务器中;如果全部位置都为1,则表示key可能存在于redis服务器中;

缓存雪崩

缓存雪崩:

Redis的缓存雪崩是指当Redis中大量缓存数据同时失效或者被清空时,大量的请求会直接打到数据库上,导致数据库瞬时压力过大,甚至宕机的情况。

造成缓存雪崩的原因主要有两个:

1.相同的过期时间:当Redis中大量的缓存数据设置相同的过期时间时,这些数据很可能会在同一时间点同时失效,导致大量请求直接打到数据库上。

2.缓存集中失效:当服务器重启、网络故障等因素导致Redis服务不可用,且缓存数据没有自动进行容错处理,当服务恢复时大量的数据同时被重新加载到缓存中,也会导致大量请求直接打到数据库上。

预防缓存雪崩的方法主要有以下几种:

1.设置不同的过期时间:可以将缓存数据的过期时间分散开,避免大量缓存数据在同一时间点失效。

2.使用加锁:可以将所有请求都先进行加锁操作,当某个请求去查询数据库时,如果还没有加载到缓存中,则只让单个线程去执行加载操作,其他线程等待该线程完成后再次进行判断,避免瞬间都去访问数据库从而引起雪崩。

3.提前加载预热:在系统低峰期,可以提前将部分热点数据加载到缓存中,这样可以避免在高峰期缓存数据失效时全部打到数据库上。

4.使用多级缓存:可以在Redis缓存之上再使用一层缓存,例如本地缓存等,当Redis缓存失效时,还能够从本地缓存中获取数据,避免直接打到数据库上。

在这里插入图片描述

本地缓存:ehcache oscache spring自带缓存 持久层框架的缓存


总结

Java进阶(锁)——锁的升级,synchronized与lock锁区别

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

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

相关文章

Fastwhisper + Pyannote 实现 ASR + 说话者识别

文章目录 前言一、faster-whisper简单介绍二、pyannote.audio介绍三、faster-whisper pyannote.audio 实现语者识别四、多说几句 前言 最近在研究ASR相关的业务&#xff0c;也是调研了不少模型&#xff0c;踩了不少坑&#xff0c;ASR这块&#xff0c;目前中文普通话效果最好的…

Scrapy与分布式开发(1.1):课程导学

Scrapy与分布式开发&#xff1a;从入门到精通&#xff0c;打造高效爬虫系统 课程大纲 在这个专栏中&#xff0c;我们将一起探索Scrapy框架的魅力&#xff0c;以及如何通过Scrapy-Redis实现分布式爬虫的开发。在本课程导学中&#xff0c;我们将为您简要介绍课程的学习目标、内容…

Verilog Coding Styles For Improved Simulation Efficiency论文学习记录

原文基于Verilog-XL仿真器&#xff0c;测试了以下几种方式对仿真效率的影响。 1. 使用 Case 语句而不是 if / else if 语句 八选一多路选择器 case 实现效率比 if / else if 提升 6% 。 2. 如果可以尽量不使用 begin end 语句 使用 begin end 的 ff 触发器比不使用 begin end …

深度相机xyz点云文件三维坐标和jpg图像文件二维坐标的相互变换函数

深度相机同时拍摄xyz点云文件和jpg图像文件。xyz文件里面包含三维坐标[x,y,z]和jpg图像文件包含二维坐标[x&#xff0c;y],但是不能直接进行变换&#xff0c;需要一定的步骤来推演。 下面函数是通过box二维框[xmin, ymin, xmax, ymax, _, _ ]去截取xyz文件中对应box里面的点云…

MyCAT学习——在openEuler22.03中安装MyCAT2(网盘下载版)

准备工作 因为MyCAT 2基于JDK 1.8开发。也需要在虚拟机中安装JDK&#xff08;JDK官网就能下载&#xff0c;我这提供一个捷径&#xff09; jdk-8u401-linux-x64.rpmhttps://pan.baidu.com/s/1ywcDsxYOmfZONpmH9oDjfw?pwdrhel下载对应的tar安装包,以及对应的jar包 安装程序包…

九州金榜|孩子厌学要怎么办?

孩子从小学到初中再到高中&#xff0c;孩子出现厌学情绪很正常&#xff0c;但是孩子出现厌学情绪后&#xff0c;就必然会影响到孩子学习成绩&#xff0c;孩子产生厌学情绪的原因有哪些呢&#xff1f;只有找准孩子厌学原因才能去帮助孩子怎样去克服孩子厌学情绪&#xff0c;下面…

day34贪心算法 part03

1005. K 次取反后最大化的数组和 简单 给你一个整数数组 nums 和一个整数 k &#xff0c;按以下方法修改该数组&#xff1a; 选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。 重复这个过程恰好 k 次。可以多次选择同一个下标 i 。 以这种方式修改数组后&#xff0c;返回数…

OSError: [WinError 1455] 页面文件太小,无法完成操作。

[问题描述]&#xff1a;OSError: [WinError 1455] 页面文件太小&#xff0c;无法完成操作。 原因1&#xff1a;线程数太大 方法&#xff1a;改小线程&#xff08;workers&#xff09;数。 原因2&#xff1a;虚拟内存太小或为0&#xff0c;调大虚拟内存。 方法&#xff1a;右键…

Vue.js 实用技巧:深入理解 Vue.mixin

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

【Linux进程间通信】共享内存

【Linux进程间通信】共享内存 目录 【Linux进程间通信】共享内存system V共享内存共享内存示意图共享内存的数据结构共享内存函数将共享内存挂接到对应的进程将共享内存取消挂接释放共享内存 共享内存的特性共享内存扩展共享内存配合管道进行使用 作者&#xff1a;爱写代码的刚…

用docker部署后端项目

一、搭建局域网 1.1、介绍前后端项目搭建 需要4台服务器&#xff0c;在同一个局域网中 1.2、操作 # 搭建net-ry局域网&#xff0c;用于部署若依项目 net-ry&#xff1a;名字 docker network create net-ry --subnet172.68.0.0/16 --gateway172.68.0.1#查看 docker network ls…

3d模型合并后一片漆黑是什么原因,怎么解决---模大狮模型网

当合并多个3D模型后&#xff0c;发现整个合并后的模型显示为一片漆黑通常是由以下几个可能的原因导致的&#xff1a; 材质设置问题&#xff1a;合并后的模型可能存在材质设置错误&#xff0c;导致模型无法正确显示。检查每个模型的材质属性&#xff0c;确保其正确设置&#xff…

老隋蓝海项目有哪些?能赚钱吗?

在创业的海洋中&#xff0c;每个人都渴望找到那片属于自己的“蓝海”&#xff0c;而“老隋蓝海项目”便是许多人心中的那片未知海域。那么&#xff0c;老隋蓝海项目究竟是指什么?它们又能否成为创业者的新财富之源? 蓝海项目的定义 我们要明白&#xff0c;蓝海项目通常指的是…

【漏洞复现】某厂商明御WEB应用防火墙任意用户登录漏洞

Nx01 产品简介 安恒明御WEB应用防火墙&#xff08;简称WAF&#xff09;是杭州安恒信息技术股份有限公司自主研发的一款专业应用安全防护产品&#xff0c;专注于为网站、APP等Web业务系统提供安全防护。 Nx02 漏洞描述 安恒明御WEB应用防火墙report.php文件存在硬编码设置的Con…

yolov7添加spd-conv注意力机制

一、spd-conv是什么&#xff1f; SPD-Conv&#xff08;Symmetric Positive Definite Convolution&#xff09;是一种新颖的卷积操作&#xff0c;它主要应用于处理对称正定矩阵&#xff08;SPD&#xff09;数据。在传统的卷积神经网络&#xff08;CNN&#xff09;中&#xff0c;…

人工智能_大模型013_AIGC生成式模型的增强检索_RAG知识补充检索_补充私域和实时场景知识_关键字检索增强---人工智能工作笔记0149

什么是RAG,RAG的意思就是,如果一套生成式AIGC大模型,你昨天训练了以后,那么今天的知识,还没有给他进行训练,那么回答的时候,他就会遗漏今天的知识,那么我们就可以通过检索的手段,把今天的知识,检索出来,然后补充道prompt中,给这个大模型.让他参考,这样就包含了今天的知识相当于…

CY8C42(1.PSoC4 Pioneer Kit开箱及基本使用)

1.开箱 最近了解到赛普拉斯有一种芯片&#xff0c;属于PSoC系列&#xff0c;与传统MCU不同&#xff0c;有点类似跨界芯片&#xff0c;于是就买来玩玩了&#xff0c;老实说用完还是很特别的&#xff0c;因为我没有用过FPGA&#xff0c;不确定是不是FPGA的开发流程&#xff08;有…

MySQL的事务与隔离级别

1. 什么是事务&#xff1f; 数据库中的事务是指对数据库执行一批操作&#xff0c;而这些操作最终要么全部执行成功&#xff0c;要么全部失败&#xff0c;不会存在部分成功的情况。这个时候就需要用到事务。 最经典的例子就是转账&#xff0c;你要给朋友小白转 1000 块钱&…

一代传奇宗庆后:把员工宠成上帝

作者&#xff1a;积溪 琥珀酒研社快评&#xff1a; 梅子真是哭了 一代传奇就此陨落 咱们又少了一个良心企业家 2月25日10时30分 娃哈哈集团创始人、董事长宗庆后 在杭州逝世&#xff0c;享年79岁 在过去一个多月的病危期间 他的病房里最显眼的 不是呼吸机、检测仪 而…

智慧城市中的公共服务创新:让城市生活更便捷

目录 一、引言 二、智慧城市公共服务创新的实践 1、智慧交通系统 2、智慧医疗服务 3、智慧教育系统 4、智慧能源管理 三、智慧城市公共服务创新的挑战 四、智慧城市公共服务创新的前景 五、结论 一、引言 随着信息技术的迅猛发展&#xff0c;智慧城市已成为现代城市发…