java中都有哪些锁?

article/2025/3/17 13:04:59

      相信大家在学习java的过程中, 一定听到过很多, 关于java中的锁 , 但这些锁并不完全都是指一把真正的锁, java中的锁有的是指锁的实现, 而有的指的是锁的特性, 下面来介绍

目录

乐观锁与悲观锁

死锁

可重入锁

读写锁

 分段锁

自旋锁

共享锁与独占锁

AQS

公平锁与非公平锁

偏向锁, 轻量级锁与重量级锁

java对象头

Synchronized

ReetrantLock


乐观锁与悲观锁

乐观锁与悲观锁其实指的是看待并发问题的角度,并不是锁实现

乐观锁 : 从名字上我们就可以看出, 乐观锁对于并发问题看待的角度是乐观的 , 它认为我们不显式加锁的操作是没有事情的, 对同一数据的并发操作是不影响的, 在更新数据时, 大多会采用自旋的方式更新数据(不断尝试更新:CAS)

悲观锁 : 顾名思义, 悲观锁认为我们对同一数据的并发操作肯定会有问题, 所以悲观锁就是采用显式加锁的方式来解决问题, 悲观的认为不加锁的并发操作肯定会有事

       在大多并发操作中 , 一般可以分为读操作和写操作 , 读操作并不会影响数据的安全性, 只有写操作会影响 , 所以, 如果在读操作很多的情况下, 我们可以采用乐观锁的思想, 同样的, 如果写操作很多的情况下, 我们就采用悲观锁的思想

        我们知道, 加锁虽然保证了安全问题, 但是同样带来了效率变低的问题 , 所以灵活运用乐观锁的思想可以提高效率, 例如CAS机制, 它采用自旋不断尝试更新的操作, 大大提升了效率, 而悲观锁的方式就是显式加各种锁(synchronized等)

死锁

     死锁指的是不同的线程相互占用对方所需的共享资源不释放, 导致两个线程卡住, 无法运行, 两个线程都在等待对方释放自己需要的资源,  出现死锁时, 不会有异常错误等, 而是导致这些线程阻塞,无法运行, 在程序设计时应该尽量避免死锁问题

       导致死锁出现的问题主要是对共享资源的抢占, 在程序时 ,我们应尽量减少锁的循环嵌套等, 通过死锁产生的一些必要条件入手, 避免死锁, 例如资源一次性分配等, 破坏请求条件, 这样可以有效避免死锁的发生

可重入锁

        可重入锁又叫递归锁, 它指的是同一个线程如果在外层获得锁时, 那么在进入内层方法时也会自动获得锁, java中的ReentrantLook和Synchronized都是可重入锁

          上面说的是什么意思呢? 我们来解释如下:

         如图 , 这里有两个同步方法A和B , 在A中调用B , A方法先拿到锁, 调用B方法 , B方法也为同步方法 , 因为Synchronized是可重入锁, 所以B方法此时也自动获得锁, 可以获得执行权. 

         那么如果不是可重入锁会怎样呢? 不是可重入锁, A调用B , 到了B执行的时候, 此时A方法没有执行完 ,不会释放锁, 但是B方法又需要这把锁, 一直等待A释放, A不释放, B也一直拿不到, 这样就形成了死锁, 造成线程卡死无法继续执行

读写锁

           跟上面的都不同, 读写锁(ReadWriterLock)指的是一种锁实现 , 它的特点是 1. 多个读者可以同时读  2. 写者必须互斥(读写也互斥)  3 . 写者优先于读者(一旦有写者, 则写者优先,后续读者等待)

        java中的 ReaderWriteLock是一个接口 

其中 , ReentrantReadWriteLock 类 实现了这个接口, 它是一个可重入读写锁, 使用ReetrantReadWriteLock实现读写锁如下 : 

public class ReentrantReadWriteDemo {private int data;private ReadWriteLock rwl = new ReentrantReadWriteLock();//写操作public void set(int data) {rwl.writeLock().lock(); //拿到写锁try {System.out.println(Thread.currentThread().getName() + "准备写入数据");this.data = data;System.out.println(Thread.currentThread().getName() + "写入" + this.data);} finally {rwl.writeLock().unlock(); //释放写锁}}//读操作public void get() {rwl.readLock().lock(); //拿到读锁try {System.out.println(Thread.currentThread().getName() + "准备读数据");System.out.println(Thread.currentThread().getName() + "读取" + this.data);} finally {rwl.readLock().unlock(); //释放读锁}}
}

 分段锁

        分段锁也也是一种锁的思想 , 指的是将数据分段, 进一步细化锁 ,在每个分段上单独加锁, 用以提高并发效率

      java中的ConcurrentHashMap 在jdk 1.8之前采用的就是分段锁的机制, 在1.8之后采用了锁分段的机制替换分段锁(关于此点在我前几篇文章: JUC常用类中提到)

那么怎样理解分段锁的好处呢 ? 如下图

      如果我们的共享数据是一个hashMap , 它底层是一个hash表, 每个hash表存储一个结点 , 采用分段锁的思想 , 在每个结点上单独加锁 , 如果此时有两个线程对hashMap进行操作, 第一个线程去操作A结点, 那么此时第二个线程是不是可以去操作A结点后续的B,C等结点 . 如果不采用分段锁的思想, 当第一个线程在hashMap中操作时, 那么其他线程只能在外等待. 这两种方式造成的效率对比可想而知

      当然分段锁的做法也有不足, 不然也不会在ConcurrentHashMap中 1.8之后弃用这种做法, 在每个分段上加锁, 可能导致浪费内存空间, 并且hashMap中竞争同一把锁概率也较小, 有可能反而导致效率低下, 但是这种做法的优点也很明显

自旋锁

       自旋锁其实并不是一种锁的特性, 自旋锁其实指的是一种思想 , 自旋就是不断循环重试, 例如 : CAS机制(关于CAS可以去看我之前的文章:并发编程核心问题),  不断循环尝试获得锁, 如果循环多次还没有拿到锁 , 那么就使该线程阻塞

       可想而知, 不断自旋会极大消耗CPU ,在低并发 , 加锁时间较短的情况下, 我们就可以采用自旋的思想, 不阻塞线程 ,提高效率 , 在jdk 1.8之后ConcurrentHashMap中就使用了自旋锁的思想 ,采用了CAS机制来添加数据

共享锁与独占锁

共享锁与独占锁同样指锁的特性 

   共享锁 : 锁可以被多个线程共享, 可以并发访问共享资源
 

   独占锁 : 也叫互斥锁, 锁只能被一个线程占用, 与其他线程互斥

         java中的Synchronied和ReentrantLook 都是独占锁, 但是在ReadWritLock接口下的实现类ReentrantReadWriteLock(读写锁)而言 , 它的读锁是共享锁, 写锁是独占锁 , 它底层的独占和共享是由 AQS 实现的, 通过不同的方法实现独占或共享

AQS

         AbstractQueuedSynchronizer(抽象队列同步器)这个类在 java.util.concurrent.locks 包, 上面说的ReentrantReadWriteLock底层就有此类的对象 , 包括ReentrantLock底层也是它

         AQS的核心思想是 , 如果被请求的共享资源空闲 , 那么则将当前请求的线程设置成有效的工作线程 , 并将共享资源设置成锁定状态 , 此时共享资源被占用 ,就需要一套线程阻塞等待以及被线程被唤醒后锁分配的机制 , 这个机制是使用CLH队列锁(CLH是三位大佬发明的, 它们名字的简称, 它是一个FIFO,先进先出的双向同步队列 ,AQS依赖它完成同步) , 就是将暂时获得不到锁的线程加到等待队列中

      在AQS中 , 存在一个采用volatile修饰的共享变量state , 它代表加锁的状态 , 这个值默认为0 , 如果有线程已经加锁了 , 那么这个值会变成 1 , 这时其他线程想拿到锁, 会通过CAS机制尝试将0 改为1 , 但此时state已经为 1 了, 所以它更改失败 ,进入等待队列

公平锁与非公平锁

 公平锁与非公平锁指的也是锁的特性

 公平锁 : 指当锁被释放后, 检查是否有线程在排队等待, 优先将锁释放给等待时间较长的线程

非公平锁 : 锁被释放后 , 不考虑排队的情况, 线程直接去尝试获得锁

       java中的Synchronized是非公平锁, ReentrantLook默认是非公平锁, 但也可以通过参数设置使它变成一个公平锁 ,底层使用AQS实现

 fair 默认为false ,设置为true则是公平锁

偏向锁, 轻量级锁与重量级锁

        它们指的是锁的状态, 是 jvm 为了提高锁的效率做的优化 ,而且它们是专门针对于synchronized而言的

偏向锁 : 当一段同步代码一直被一个线程所访问, 那么此线程下次到来将会自动获得锁 , 降低此线程获得锁的代价

轻量级锁 : 当锁是偏向锁时, 此时来了另外一个线程, 这时这把锁升级为轻量级锁, 后续的所有线程不进行阻塞 ,通过自旋的方式尝试获得锁

重量级锁 : 当锁是轻量级锁时 , 此时另一个线程自旋了很多次还是没有拿到锁 , 那么这个线程此时会阻塞, 或者就是访问的线程过多, 那么此时锁升级为重量级锁 , 重量级锁将后面访问的线程都进行阻塞

这几种锁状态都在 java 的对象头中标注

java对象头

         在HotSpot虚拟机中 , 对象可分为3部分区域 : 对象头、实例数据和对齐填充  ,  对象头是实现Synchronize锁对象的基础 , Java对象头一般占有两个机器码(在 32 位虚拟机中,1 个机器码等于 4 字节,也就是32bit),对象头中包含了很多信息, 如下 : 

      可见, 锁的状态在对象头中是有标注的, 对于锁是轻量级锁还是重量级锁等

Synchronized

          java提供的一种原子性内置锁(关键字), 每个对象都可以使用它当作同步监视器, 当线程进入使用Synchronized修饰的代码块中会自动获取内部锁 , 此时其他线程想要访问此同步代码时只能被阻塞, 等待锁的释放(前一个线程执行完, 或者出现异常,调用了wait()方法等), 在进入 synchronized 会从 主内存把变量读取到自己工作内存,在退出的时候会把工作内存的值写入到主内存,保证了原子性。

       synchronized的实现是通过字节码指令完成的 , 我们可以通过反编译去看具体的底层实现

      代码块的同步是利用 monitorenter 和 monitorexit 这两个字节码指令。在虚拟机执行到monitorenter 指令时,首先要尝试获取对象的锁。 当前线程拥有了这个对象的锁,把锁的计数器+1;当执行 monitorexit 指令时将模计数器-1;当计数器为 0 时,锁就被释放了.

      另外synchronized 通过在对象头设置标记,达到了获取锁和释放锁的目的

ReetrantLock

      前面提到, ReetrantLock是一个类 , 使用 CAS + AQS实现, 它可以是公平锁, 也可以是非公平锁, 它用于显示的加锁和释放锁

          通过名字也可以看出 , 它是一个可重入锁 , 调用 lock() 方法加锁的过程如下 : 

    如果有线程通过CAS自旋拿到了锁, 那么将 底层的state状态改为1 , 其他线程通过CAS自旋发现更改不了 state ,就会进入到一个等待队列中

unlock() : 释放锁

通过头结点的状态判断唤醒下个与头结点关联的线程 


http://chatgpt.dhexx.cn/article/TYX4kbej.shtml

相关文章

java线程锁有哪几种_Java有多少种锁?分为哪几种?

Java有多少种锁?分为哪几种?你知道么?小编今天给大家带来Java面试题之几种锁的介绍,希望大家在开发的工作中能深刻理解锁的概念。 1、自旋锁: 自旋锁在JDK1.6之后就默认开启了。基于之前的观察,共享数据的锁定状态只会…

几种常见锁的介绍

参考链接:《这样来解释Java中的并发和锁,涨知识了》 锁 一、悲观锁与乐观锁1.1 悲观锁1.2 乐观锁简单探究一下CAS算法的底层CPU级别的LOCK实现方式 1.3 两种锁的使用场景 二、独占锁与共享锁2.1 独占锁2.2 共享锁 三、公平锁和非公平锁3.1 公平锁3.2 非公…

19-你知道哪几种锁?分别有什么特点?

首先会对锁的分类有一个整体的概念,了解锁究竟有哪些分类标准。 锁的 7 大分类 需要首先指出的是,这些多种多样的分类,是评价一个事物的多种标准,比如评价一个城市,标准有人口多少、经济发达与否、城市面积大小等。而…

mysql数据库锁有哪几种_Mysql数据库的分布式锁有哪几种?

原标题:Mysql数据库的分布式锁有哪几种? 文章内容 作者:jstu 文章来自:博客 链接:https://blog.csdn.net/lovexiaotaozi/article/details/83819916 你可能喜欢的文章 1Mysql性能优化一:SQL语句性能优化 悲观…

锁的分类和介绍

java中的各种锁详细介绍 转自:https://blog.csdn.net/axiaoboge/article/details/84335452 Java提供了种类丰富的锁,每种锁因其特性的不同,在适当的场景下能够展现出非常高的效率。本文旨在对锁相关源码(本文中的源码来自JDK 8&a…

java线程锁有哪几种_java中常见的几种锁有哪些

公平锁/非公平锁 公平锁是指多个线程按照申请锁的顺序来获取锁。 非公平锁是指多个线程获取锁的顺序,并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁,有可能,会造成优先级反转或者饥饿现象。 独享锁/共享锁 …

常见的各种锁总结

常见的各种锁 一、常见锁简单说明 1、悲观锁 悲观锁认为自己在使用数据的时候一定有别的线程来修改数据,在获取数据的时候先加锁,确保数据的安全性。 锁实现:关键字synchronized、Lock接口的实现 使用场景:写操作比较多&#xff0…

BioGRID 互作数据库

01 — BioGRID BioGRID 是 Biological General Repository for Interactionh Datasets 的缩写(网址为 https://thebiogrid.org),是一个公开的数据库,主要记录、整理包括蛋白、遗传和化学互作的数据,涵盖人类和所有主要…

Fuzzing论文:Reinforcement Learning-based Hierarchical Seed Scheduling for Greybox Fuzzing

Reinforcement Learning-based Hierarchical Seed Scheduling for Greybox Fuzzing 整体内容论文内容多级代码覆盖指标(用于种子聚类)分层种子调度策略实验 论文题目Reinforcement Learning-based Hierarchical Seed Scheduling for Greybox Fuzzing工具…

BIO的理解

前言 bio:b有两说,一为base,jdk中最早抽象出的io体系;一为block,jdk 1.0 中的io体系是阻塞的。所以两说皆有道理,一般我们认为b取block之意 nio:n也有两说,一为new,针对…

BIO初步学习

1.一个服务端一个客户端 具体代码实现 服务端 public class Server {public static void main(String[] args) {try {ServerSocket serverSocket new ServerSocket(9999);Socket socket serverSocket.accept();InputStream inputStream socket.getInputStream();Buffered…

12.BIO详解

Java BIO 就是传统的 java io 编程, 其相关的类和接口在 java.io 中. BIO 编程简单流程 服务器端启动一个 ServerSocket.客户端启动 Socket 对服务器进行通讯, 默认情况下服务器需要对每个客户建立一个县线程与之通讯.客户端发出请求后, 先咨询服务器是否有线程响应, 如果没有…

bioinformatics小技巧

文章目录 1. 软件安装1.1 linux上python2的安装1.2 Mercurial 安装及使用1.3 tRNAscan的安装和使用1.4 Linux上安装miniconda 2.数据下载2.1 linux上通过ftp下载一个文件夹下的全部文件2.2 GEO数据库数据下载 3.操作系统3.1 Windows下将R设置为环境变量。3.2 Linux 下怎样快速查…

brat标注的ann文件,转为BIO序列标注

这个地方真的好少有人写到,踩了好久的坑都不知道怎么解决。 首先,在用brat自带的转换序列标注的文件时,运行程序 1、python2 anntoconll.py ../data/data_new/corpoa.txt 报错: File "anntoconll.py", line 154, in…

BIO学习笔记

视频地址:https://www.bilibili.com/video/BV1gz4y1C7RK?fromsearch&seid15021234423448500976 2. JAVA BIO深入剖析 Java BIO 就是传统的 java io 编程,其相关的类和接口在 java.io。 BIO(blocking I/O) : 同步阻塞,服务器…

BIO实例

使用 BIO 模型编写一个服务器端,监听 6666 端口,当有客户端连接时,就启动一个线程与之通讯。要求使用线程池机制改善,可以连接多个客户端.服务器端可以接收客户端发送的数据(telnet 方式即可)。 图 代码演示: package…

利用 bioconda 管理生物信息软件

利用 bioconda 管理生物信息软件 如需视频讲解,请移步:一只小蛮要 【要知道 bioinfo】利用 bioconda 管理生物信息软件 1 了解 conda,anaconda,miniconda,bioconda 1.1 conda conda是一个软件模块管理工具&#xff…

IMex和IntAct数据库简介

欢迎关注微信公众号《生信修炼手册》! 蛋白质相互作用的数据库非常的多,比如DIP, MINT, IntAct, BioGRID等,不同数据库中的信息存在了大量的冗余,而且在不同数据库之间进行检索也非常的费力,为了减少不同数据库的冗余&#xff0c…

【Bio】基础生物学 - 基因 gene

文章目录 1. DNA 脱氧核糖核酸、RNA 核糖核酸1.1 核苷酸1.2 脱氧核糖核酸1.3 核糖核酸 2. 基因2.1 基因组2.2 染色体2.3 基因与脱氧核苷酸的牵连2.4 基因与DNA的牵连2.5 基因与染色体的牵连 Ref 1. DNA 脱氧核糖核酸、RNA 核糖核酸 1.1 核苷酸 核苷酸 (Nucleotide) \blue{\tex…

Bioedit 使用

1. 下载: 地址一搜就有,软件界面如下 BioEdit Download - Research software utility for creating and editing biological sequences 2. 酶切位点分析 构建过表达质粒、双荧光素酶质粒必用功能。以人的MYOD1基因为例 ,分析酶切位点。 &…