可重入锁:ReentrantLock
在学JUC的时候,听到可重入锁这个词,不理解它的概念,网上搜索一番,还是有点迷糊,所以自己再来做一下笔记,理一理思路。

一、锁是什么?

我们这里提到的锁,是指把所需要的代码块资源,或数据锁上,在操作访问他们的时候只允许一个线程去做操作。最终结果是为了保证cpu计算结果的正确性。

二、可重入锁与不可重入锁的区别?

1、不可重入锁:

只判断这个锁有没有被锁上,只要被锁上,申请锁的线程都会被要求等待。实现简单

2、可重入锁:

不仅判断锁有没有被锁上,还会判断锁是哪个线程锁上的,当就是当前锁上的时候,那么他依旧可以再次访问临界资源,并把加锁次数加一。

设计了加锁次数,以实现在解锁的时候,可以确保所有加锁的过程都解锁了,其他线程才能访问。不然没有加锁的参考值,也就不知道什么时候解锁?解锁多少次?才能保证本线程已经访问完临界资源了可以唤醒其他线程访问了。实现相对复杂。

3、结论:

这个重入的概念就是,拿到锁的线程能不能多次以不同的方式访问临界资源而不出现死锁等相关问题。经典之处就在于判断了需要使用锁的线程是否为加锁的线程。如果是,则拥有重(chong)入的能力。

三、下面使用代码来说明

1、不可重入锁的理解:

public class Test{Lock lock = new Lock();public void methodA(){lock.lock();...........;methodB();...........;lock.unlock();}public void methodB(){lock.lock();...........;lock.unlock();}
}

当A方法获取lock锁去锁住一段需要做原子性操作的B方法时,如果这段B方法又需要加锁去做原子性操作,那么A方法就必定要与B方法出现死锁。这种会出现问题的重入一把锁的情况,叫不可重入锁。

A方法需要等B方法执行完才能解锁,但是B方法想执行完代码又必须要lock锁来加锁。A的锁未解锁前,其他代码块无法使用此锁来加锁。这是由这个不可重入锁决定的。

2、不可重入锁的加锁与解锁(实现):

public class Lock{private boolean isLocked = false;public synchronized void lock() throws InterruptedException{while(isLocked){    wait();}isLocked = true;}public synchronized void unlock(){isLocked = false;notify();}
}

那么平时我们又有需要重入一把锁的需求!!!!比如A方法是个原子性操作,但它有需要调用B方法的原子性操作,他们还争抢的是同一个临界资源,因此需要同一把锁来加锁

(ps:争抢同一临界资源的实质就是对同一把锁的争抢)

针对此情况,就有了可重入锁的概念。

3、可重入锁的实现:

public class Lock{boolean isLocked = false;Thread  lockedBy = null;//临界资源被哪个线程锁住了int lockedCount = 0;public synchronized void lock()throws InterruptedException{Thread thread = Thread.currentThread();//加锁时,先获取当前线程。(识别谁需要锁)while(isLocked && lockedBy != thread){wait();}isLocked = true;lockedCount++;lockedBy = thread;}public synchronized void unlock(){if(Thread.currentThread() == this.lockedBy){lockedCount--;if(lockedCount == 0){isLocked = false;notify();}}}
}

现在我们来理解这段代码,先看加锁

3.1、lock()

 isLocked:锁的状态。lockedBy:临界资源被哪个线程锁住了。Thread thread = Thread.currentThread();    加锁时,先获取当前线程,识别谁需要锁**while 判断:当临界资源已被锁上,但当前请求锁的线程又不是之前锁上临界资源的线程。那么当前请求锁的线程需要等待。**
while(isLocked && lockedBy != thread){wait();
}

注意上面是个while,并且是个wait,因此当线程请求不到锁的时候,就wait了。

不满足while判断条件的有3种情况:

  1. 当前锁没有线程使用,即 isLocked == false。
  2. 当前锁有线程使用 isLocked == true,当前请求锁的线程就是现在正在使用锁的线程 lockedBy == thread。
  3. 当前锁没有线程使用,当前请求锁的线程就是现在正在使用锁的线程。(不可能出现,“当前锁莫瑶线程使用 isLocked == false” 与 "请求锁的线程就是现在正在用锁的线程 isLocked == true"两者之间是矛盾的)

在while条件不满足时,那么当前线程可以加锁:

isLocked = true;
lockedCount++;
lockedBy = thread;

当前请求锁的线程先把锁加上,然后把上锁次数+1,然后把自己(本线程)赋值给lockedBy,以说明当前谁用了这把锁方便之后重入的时候做while判断。

3.2、unlock()

再来看解锁:

public synchronized void unlock(){if(Thread.currentThread() == this.lockedBy){lockedCount--;if(lockedCount == 0){isLocked = false;notify();}}
}

首先看看要求解锁的线程是不是当前正在使用锁的线程。不是则什么也不做。(这个判断使为了保证当前要解锁的线程必须也是加锁的线程,谁加的谁来解。否则其他线程也能随意的执行unlock代码就能解锁,那这样相当于谁都有一把钥匙了,加锁也失去了意义)

如果解锁的就是加锁的线程。
那么把加锁次数减一。
然后在判断加锁次数有没有变为0。
变为 0 说明,这个锁已经完全解锁了。
锁状态标志 islocked 就可以复位为 false了。
并且随机唤醒某个被 wait() 等待的线程 :notify()

这就是重入锁的设计。

再次回顾上文所述的

4、可重入锁和不可重入锁的区别:

不可重入锁:只判断这个锁有没有被锁上,只要被锁上申请锁的线程都会被要求等待。实现简单

可重入锁:不仅判断锁有没有被锁上,还会判断锁是谁锁上的,当就是自己锁上的时候,那么他依旧可以再次访问临界资源,并把加锁次数加一。

设计了加锁次数,以在解锁的时候,可以确保所有加锁的过程都解锁了,其他线程才能访问。不然没有加锁的参考值,也就不知道什么时候解锁?解锁多少次?才能保证本线程已经访问完临界资源了可以唤醒其他线程访问了。实现相对复杂。

重入:指拿到锁的线程能不能多次以不同的方式访问临界资源而不出现死锁等相关问题。经典之处在于判断了需要使用锁的线程是否为加锁的线程。如果是,则拥有重入的能力。

怎么样,现在是不是理解更清晰了?

参考原文:https://blog.csdn.net/qq_29519041/article/details/86583945

对可重入锁和不可重入锁的理解相关推荐

  1. java锁(公平锁和非公平锁、可重入锁(又名递归锁)、自旋锁、独占锁(写)/共享锁(读)/互斥锁、读写锁)

    前言 本文对Java的一些锁的概念和实现做个整理,涉及:公平锁和非公平锁.可重入锁(又名递归锁).自旋锁.独占锁(写)/共享锁(读)/互斥锁.读写锁 公平锁和非公平锁 概念 公平锁是指多个线程按照申请 ...

  2. 乐观锁和悲观锁,可重入锁和不可重入锁(1)

    乐观锁和悲观锁,可重入锁和不可重入锁(1) 前言 感觉有一段时间没有写博客了呢.还是再接再厉吧,适当程度的总结能让我自己能够更加深入地巩固和理解自己所学习的一切. 还有,我很懒,而且我还是比较喜欢写日 ...

  3. Java 中15种锁的介绍:公平锁,可重入锁,独享锁,互斥锁,乐观锁,分段锁,自旋锁等等...

    http://blog.51cto.com/13919357/2339446 Java 中15种锁的介绍 在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类.介绍的内容 ...

  4. Java进阶:ReentrantLock实现原理解析(公平锁、非公平锁、可重入锁、自旋锁)

    概述 本篇将介绍公平锁.非公平锁.可重入锁.自旋锁相关理论知识,同时结合相关源码和Demo进行解析,主要是以ReentrantLock作为例子. 公平锁 公平锁定义 公平锁是指线程按照申请所的顺序来获 ...

  5. 可重入锁和不可重入锁

    转自https://www.cnblogs.com/dj3839/p/6580765.html 锁的简单应用 用lock来保证原子性(this.count++这段代码称为临界区) 什么是原子性,就是不 ...

  6. 线程调度、公平锁和非公平锁、乐观锁和悲观锁、锁优化、重入锁

    1. 线程调度 线程调度指的就是给线程分配使用处理器的过程.主要的调度方式有两种:协同式调度和抢占式调度. 1.1 协同式调度 线程完成自己的任务之后主动通知系统切换到另一个线程上. 优点: 实现简单 ...

  7. Java锁之可重入锁和递归锁

    Java锁之可重入锁和递归锁 目录 Java锁之可重入锁和递归锁基本概念 Java锁之可重入锁和递归锁代码验证 小结 理论,代码,小结,学习三板斧. 1. Java锁之可重入锁和递归锁基本概念 可重入 ...

  8. Java 中15种锁的介绍:公平锁,可重入锁,独享锁,互斥锁,乐观锁,分段锁,自旋锁等等

    Java 中15种锁的介绍 在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类.介绍的内容如下: 公平锁 / 非公平锁 可重入锁 / 不可重入锁 独享锁 / 共享锁 互 ...

  9. java -锁(公平、非公平锁、可重入锁【递归锁】、自旋锁)

    1.公平锁.非公平锁 2.可重入锁(递归锁) 3.自旋锁 AtomicReference atomicReference = new AtomicReference();//原子引用线程 下面代码5秒 ...

  10. 不可重入锁和可重入锁

    不可重入锁也叫自旋锁 指当一个方法调用了锁之后,如持有本锁的另一个方法也想执行,将会进入等待.那么想要使用这个方法必须先释放锁方可调用 public class Lock{private boolea ...

最新文章

  1. linux id 命令 显示用户id和组id信息
  2. python入门经典例题-Python入门经典练习题
  3. yii2-按需加载并管理CSS样式/JS脚本
  4. 关于移动,联通,电信的区分。
  5. php利用openssl实现RSA非对称加密签名
  6. 如何连接oracle xe_为什么应始终将连接池与Oracle XE一起使用
  7. Linux网络编程——tcp并发服务器(I/O复用之select
  8. Python内置数据类型之Tuple
  9. 从java到C++入门
  10. 群晖能从linux备份数据吗,通过rsync将Linux服务器数据备份到群晖
  11. 算法设计和数据结构学习_2(常见排序算法思想)
  12. 编译PBRT-v3源码
  13. 实用主义学python爬虫_麻瓜编程 实用主义学Python2018
  14. 短代码的java小游戏_java编写的简单移动方块小游戏代码
  15. Material Design-Surface平面第二篇
  16. 免校准的电量计量芯片_免校准电能计量芯片,让家电智能化更简单
  17. GVRP和VTP的比较与区别
  18. 固态硬盘raw格式数据能恢复吗(图文)
  19. Javascript事件绑定的几种方式
  20. 用户同步管理及集群初始配置-集群搭建步骤7

热门文章

  1. Python之包管理工具
  2. easyUI layout 中使用tabs+iframe解决请求两次方法
  3. c++ list sort
  4. open×××+Mysql+PAM构建强大的***系统
  5. 随机过程及其在金融领域中的应用 第三章 习题 及 答案
  6. flex白板之图形绘制函数
  7. 实现三元组表示的两个稀疏矩阵的加法_K-BERT | 基于知识图谱的语言表示模型
  8. android viewpager 间隔,viewpager 系统兼容 clipChildren 页卡间距
  9. bootstrap五星评分_如何用纯代码实现评分星级显示?
  10. scala 线性回归_Scala的特征线性化