常见的几种锁

article/2025/3/17 13:03:38

1.悲观锁  for update

    悲观锁认为每次查询数据数据都会造成数据的更新或者丢失问题,所以每次查询都会加上排它锁。

    

    如图所示,当两条线程同时访问该sql语句时,可能会造成脏读数据user_money为原来的两倍(假设线程一执行完第一句等待,线程二将两句全部执行完,这时线程一如果继续执行则会脏读数据)

   使用悲观锁则通过在其后加for update后,仅允许一个连接查询数据也就是只要一个连接获得锁后,其他连接则只能等待该锁的释放。

   缺点:每次都只有一个连接进行操作,效率非常低,适合查询量低的情况。

2.乐观锁  version

   乐观锁认为每次查询都不会造成数据更新丢失,使用版本字段控制。(version机制)

   

    如图所示,每一次执行更新操作时,都要将version字段加一,这样其他连接就不能通过之前的version查找到该条数据,从而保证不重复读写。

   优点:可并发运行,效率高          缺点:需要增加一个维护字段version,而且可能会出现查不到数据的情况。

3.重入锁 ReentrantLock

  

重入锁,也叫做递归锁,指的是同一线程 外层函数获得锁之后 ,内层递归函数仍然有获取该锁的代码,但不受影响。

非重入锁进行以上操作的话就会产生死锁。

简单的说就是当外层获取到某一把锁,若该锁是可重入锁,则默认内层所有代码都获取了该锁。

可重入锁有一个状态计数器,每当获取一次该锁,计数器的数值就会加一。每次释放锁,计数器的数值就会减一,只有当计数器的值减到0的时候,才会真正释放该锁,其他线程才可重新获取该锁。

如果一把锁是不可重入锁时,当外层获取该锁后,内层的代码再次获取该锁时,由于外层没有释放,内层就获取不到而阻塞,导致程序等待,而外层也因此无法释放锁,就产生了死锁。

 1 public class Test02 extends Thread {
 2     ReentrantLock lock = new ReentrantLock();
 3     public void get() {
 4         lock.lock();
 5         System.out.println(Thread.currentThread().getId());
 6         set();
 7         lock.unlock();
 8     }
 9     public void set() {
10         lock.lock();
11         System.out.println(Thread.currentThread().getId());
12         lock.unlock();
13     }
14     @Override
15     public void run() {
16         get();
17     }
18     public static void main(String[] args) {
19         Test ss = new Test();
20         new Thread(ss).start();
21         new Thread(ss).start();
22         new Thread(ss).start();
23     }
24  
25 }

 

4.读写锁 ReentrantReadWriteLock

    假设你的程序中涉及到对一些共享资源的读和写操作,且写操作没有读操作那么频繁。在没有写操作的时候,两个线程同时读一个资源没有任何问题,所以应该允许多个线程能在同时读取共享资源。

    但是如果有一个线程想去写这些共享资源,就不应该再有其它线程对该资源进行读或写(也就是说:读-读能共存,读-写不能共存,写-写不能共存)。这就需要一个读/写锁来解决这个问题。

    常用于缓存设计。

  1 public class Cache {
  2     private static volatile Map<String,Object> map=new HashMap<>();
  3  
  4     private static ReentrantReadWriteLock reentrantReadWriteLock=new ReentrantReadWriteLock();
  5  
  6     private static Lock r=reentrantReadWriteLock.readLock();
  7     private static Lock w=reentrantReadWriteLock.writeLock();
  8     /**
  9      * 写
 10      * @param key
 11      * @param object
 12      */
 13     public static void put(String key,Object object){
 14         try{
 15 //            w.lock();
 16             System.out.println("正在写入key:"+key+",value:"+object+"开始。。");
 17             Thread.sleep(100);
 18             Object obj=map.put(key,object);
 19             System.out.println("写入key:"+key+",value:"+object+"结束。。");
 20         } catch (Exception e) {
 21             e.printStackTrace();
 22         } finally {
 23 //            w.unlock();
 24         }
 25     }
 26  
 27     /**
 28      * 读
 29      * @param key
 30      * @return
 31      */
 32     public static Object get(String key){
 33         try{
 34 //            r.lock();
 35             System.out.println("正在读取key:"+key+"开始。。");
 36             Thread.sleep(100);
 37             Object obj=map.get(key);
 38             System.out.println("读取key:"+key+",value:"+obj+"结束。。");
 39             return obj;
 40         } catch (Exception e) {
 41             e.printStackTrace();
 42         } finally {
 43 //            r.unlock();
 44         }
 45         return null;
 46     }
 47  
 48     public static void main(String[] args) {
 49         new Thread(new Runnable() {
 50             @Override
 51             public void run() {
 52                 for (int i = 0; i < 10; i++) {
 53                     Cache.put(i+"",i+"");
 54                 }
 55             }
 56         }).start();
 57         new Thread(new Runnable() {
 58             @Override
 59             public void run() {
 60                 for (int i = 0; i < 10; i++) {
 61                     System.out.println(Cache.get(i+""));
 62                 }
 63             }
 64         }).start();
 65     }
 66 }
 67  
 68 输出如下:
 69 正在写入key:0,value:0开始。。
 70 正在读取key:0开始。。
 71 读取key:0,value:null结束。。
 72 写入key:0,value:0结束。。
 73 正在写入key:1,value:1开始。。
 74 null
 75 正在读取key:1开始。。
 76 读取key:1,value:null结束。。
 77 null
 78 写入key:1,value:1结束。。
 79 正在写入key:2,value:2开始。。
 80 正在读取key:2开始。。
 81 写入key:2,value:2结束。。
 82 读取key:2,value:null结束。。
 83 null
 84 正在写入key:3,value:3开始。。
 85 正在读取key:3开始。。
 86 写入key:3,value:3结束。。
 87 读取key:3,value:null结束。。
 88 null
 89 正在读取key:4开始。。
 90 正在写入key:4,value:4开始。。
 91 写入key:4,value:4结束。。
 92 读取key:4,value:4结束。。
 93 4
 94 正在写入key:5,value:5开始。。
 95 正在读取key:5开始。。
 96 读取key:5,value:null结束。。
 97 null
 98 正在读取key:6开始。。
 99 写入key:5,value:5结束。。
100 正在写入key:6,value:6开始。。
101 写入key:6,value:6结束。。
102 正在写入key:7,value:7开始。。
103 读取key:6,value:6结束。。
104 6
105 正在读取key:7开始。。
106 读取key:7,value:null结束。。
107 null
108 正在读取key:8开始。。
109 写入key:7,value:7结束。。
110 正在写入key:8,value:8开始。。
111 写入key:8,value:8结束。。
112 正在写入key:9,value:9开始。。
113 读取key:8,value:8结束。。
114 8
115 正在读取key:9开始。。
116 写入key:9,value:9结束。。
117 读取key:9,value:9结束。。
118 9

    可以看到,在进行写操作的时候进行了读取操作,这样就造成数据不安全,将注释掉的锁代码打开后就可以实现读写分离保证数据安全。

5. CAS 无锁机制

    CAS :  Compare And Swap  原子类底层使用CAS无锁机制实现保证线程安全,CAS无锁机制效率比有锁机制高。

(1)与锁相比,使用比较交换(下文简称CAS)会使程序看起来更加复杂一些。但由于其非阻塞性,它对死锁问题天生免疫,并且,线程间的相互影响也远远比基于锁的方式要小。

    更为重要的是,使用无锁的方式完全没有锁竞争带来的系统开销,也没有线程间频繁调度带来的开销,因此,它要比基于锁的方式拥有更优越的性能。

(2)无锁的好处:

第一,在高并发的情况下,它比有锁的程序拥有更好的性能;

第二,它天生就是死锁免疫的。

就凭借这两个优势,就值得我们冒险尝试使用无锁的并发。

(3)CAS算法的过程是这样:它包含三个参数CAS(V,E,N): V表示要更新的变量,E表示预期值,N表示新值。仅当V值等于E值时,才会将V的值设为N,如果V值和E值不同,则说明已经有其他线程做了更新,则当前线程什么都不做。最后,CAS返回当前V的真实值。

(4)CAS操作是抱着乐观的态度进行的,它总是认为自己可以成功完成操作。当多个线程同时使用CAS操作一个变量时,只有一个会胜出,并成功更新,其余均会失败。失败的线程不会被挂起,仅是被告知失败,并且允许再次尝试,当然也允许失败的线程放弃操作。基于这样的原理,CAS操作即使没有锁,也可以发现其他线程对当前线程的干扰,并进行恰当的处理。

(5)简单地说,CAS需要你额外给出一个期望值,也就是你认为这个变量现在应该是什么样子的。如果变量不是你想象的那样,那说明它已经被别人修改过了。你就重新读取,再次尝试修改就好了。

(6)在硬件层面,大部分的现代处理器都已经支持原子化的CAS指令。在JDK 5.0以后,虚拟机便可以使用这个指令来实现并发操作和并发数据结构,并且,这种操作在虚拟机中可以说是无处不在。

 

6.自旋锁 AtomicReference

    

 1 class SpinLock{
 2     private AtomicReference<Thread> sign=new AtomicReference<>();
 3     public void lock(){
 4         Thread thread=Thread.currentThread();
 5         while(!sign.compareAndSet(null,thread)){
 6  
 7         }
 8     }
 9  
10     public void unlock(){
11         Thread thread=Thread.currentThread();
12         sign.compareAndSet(thread,null);
13     }
14 }
15 public class Test implements Runnable{
16     static int sum;
17     private SpinLock lock;
18     public Test(SpinLock lock){
19         this.lock=lock;
20     }
21     public static void main(String[] args) throws InterruptedException {
22         SpinLock spinLock=new SpinLock();
23         for (int i = 0; i < 100; i++) {
24             Test test=new Test(spinLock);
25             Thread thread=new Thread(test);
26             thread.start();
27         }
28         Thread.sleep(1000);
29         System.out.println(sum);
30     }
31  
32     @Override
33     public void run() {
34         this.lock.lock();
35         this.lock.lock();
36         sum++;
37         this.lock.unlock();
38         this.lock.unlock();
39     }
40 }

    

当一个线程调用这个不可重入的自旋锁去加锁的时候没问题,当再次调用lock()的时候,因为自旋锁的持有引用已经不为空了,该线程对象会误认为是别人的线程持有了自旋锁

使用了CAS原子操作,lock函数将owner设置为当前线程,并且预测原来的值为空。unlock函数将owner设置为null,并且预测值为当前线程。

当有第二个线程调用lock操作时由于owner值不为空,导致循环一直被执行,直至第一个线程调用unlock函数将owner设置为null,第二个线程才能进入临界区。

由于自旋锁只是将当前线程不停地执行循环体,不进行线程状态的改变,所以响应速度更快。但当线程数不停增加时,性能下降明显,因为每个线程都需要执行,占用CPU时间。如果线程竞争不激烈,并且保持锁的时间段。适合使用自旋锁。

 

7.分布式锁

如果想在不同的jvm中保证数据同步,使用分布式锁技术。

有数据库实现、缓存实现、Zookeeper分布式锁

具体各种实现方式请自行百度 

转载于:https://www.cnblogs.com/xiaobai1202/p/10803597.html


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

相关文章

java中都有哪些锁?

相信大家在学习java的过程中, 一定听到过很多, 关于java中的锁 , 但这些锁并不完全都是指一把真正的锁, java中的锁有的是指锁的实现, 而有的指的是锁的特性, 下面来介绍 目录 乐观锁与悲观锁 死锁 可重入锁 读写锁 分段锁 自旋锁 共享锁与独占锁 AQS 公平锁与非公平锁…

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

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

几种常见锁的介绍

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

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

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

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

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

锁的分类和介绍

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

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

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

常见的各种锁总结

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

BioGRID 互作数据库

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

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

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

BIO的理解

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

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序列标注

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

BIO学习笔记

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

BIO实例

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

利用 bioconda 管理生物信息软件

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

IMex和IntAct数据库简介

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