小工具      在线工具  汉语词典  css  js  c++  java

Java可重入锁与不可重入锁

Java 额外说明

收录于:99天前

所谓可重入锁,就是指线程单元。一个线程获取对象锁后,该线程可以再次获取该对象的锁,但其他线程则不能。

synchronized 和 ReentrantLock 都是可重入锁。

可重入锁的目的是防止死锁。

实现原理是通过为每个锁关联一个请求计数器和一个占有它的线程。当计数为0时,认为锁是未被占有的;线程请求一个未被占有的锁时,JVM将记录锁的占有者,并且将请求计数器置为1 。

如果同一个线程再次请求锁,则计数会递增;

每次占用线程退出同步块时,计数器值都会递减。直到计数器达到 0 时,锁才被释放。

关于父类和子类的锁的重入:子类重写父类的同步方法,然后调用父类中的方法。如果此时没有可重入锁,那么这段代码就会产生死锁(很可能很容易理解)。

例子:

比如A类中有一个方法:public synchronized methodA1(){

方法A2();

}

和公共同步方法A2(){

//详细操作

}

也是A类中的一种同步方法,当当前线程调用A类的对象methodA1同步方法时,如果其他线程没有获得A类的对象锁,那么当前线程就获得当前A类对象的锁,然后执行methodA1同步方法。当body中调用methodA2同步方法时,当前线程可以再次获取A类对象的锁,但其他线程则不能。这是一个可重入锁。

代码演示:

不可重入锁:

public class Count{
    Lock lock = new Lock();
    public void print(){
        lock.lock();
        doAdd();
        lock.unlock();
    }
    public void doAdd(){
        lock.lock();
        //do something
        lock.unlock();
    }
}

要使用此锁:

public class Count{
    Lock lock = new Lock();
    public void print(){
        lock.lock();
        doAdd();
        lock.unlock();
    }
    public void doAdd(){
        lock.lock();
        //do something
        lock.unlock();
    }
}

当前线程执行print()方法时,首先获取锁。那么,当执行doAdd()方法时,doAdd()中的逻辑就无法执行,必须先释放锁。这个例子是不可重入锁定的一个很好的例子。

可重入锁:

接下来我们设计一个可重入锁

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();
            }
        }
    }
}

所谓可重入,是指线程可以进入它已经拥有的锁的同步代码块。

我们设计两个线程来调用 print() 方法。第一个线程调用print()方法获取锁,并进入lock()方法。由于初始lockedBy为null,因此不会进入while并挂起当前线程,而是会递增当前线程。测量第一个线程的lockedCount并记录lockBy。然后第一个线程进入doAdd()方法。既然是同一个进程,就不会进入while而hang。然后它增加lockedCount。当第二个线程尝试加锁时,它不会获取锁,因为isLocked=true。直到第一个线程调用unlock()两次将lockCount减至0之前,标志isLocked不会设置为false。

可重入锁的概念和设计思想大体是相同的,Java中的可重入锁ReentrantLock的设计思想也是相同的。

Synchronized 和 ReentrantLock 都是可重入锁。

ReentrantLock和synchronized的比较:

1.前者使用灵活,但必须手动打开和释放锁

2、前者扩展性好,有时间锁等待(tryLock())、可中断锁等待(lockInterruptically())、锁投票等,适合锁竞争高、条件变量多的地方。

3、前者提供了可轮询的锁请求,可以尝试获取锁(tryLock())。如果失败,则释放所获取的锁。有完善的错误恢复机制,避免死锁。

转自:https://blog.csdn.net/qq_39101581/article/details/82144499?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-7.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu- 7.无案例

. . .

相关推荐

额外说明

pyinstaller打包遇到:AttributeError: 'str' object has no attribute 'decode' 报错问题解决办法

  目录  1、问题复现 (1)python2编译环境下运行  (2)python3编译环境下运行  2、解决办法  1、问题复现 问题原因:Python2和Python3版本的区别导致的。 #!/usr/bin/python # -*- coding:

额外说明

SQL Server初始化表:删除数据及主键复位

在做项目的过程中,经常会遇到一种情况,开发和测试过程中,会往业务表中添加很多测试数据,等到项目重新发布时需要删除数据并且让主键重新从1开始,若是表比较少还好办,多起来就不好玩了。昨天就碰到这么个事儿,总共50多张表,弄了个将近半个小时,弄完之后想想有没有

额外说明

V4-07 sso连接数据库完整+ 优势测试体现

接上:  SSO单点登录中篇(无数据库版) 目录 完善SSO单点登录系统(+数据库) 01演示单点登录系统的优势

额外说明

【C++】详细讲解C++的程序流程控制~

大家好,我是卷心菜。本篇主要讲解C++的流程控制,如果您看完文章有所收获,可以三连支持博主哦~,嘻嘻。 文章目录 一、前言 二、选择结构 1、if语句 2、三目运算符 3、switch语句 三、循环结构 1、while循环语句 2、do...while循

额外说明

flex定时处理

       var timer:Timer = new Timer(1000, 1800);        //当合法用户登陆成功之后跳转到状态nextModule;        timer.addEventListener(TimerEvent.T

额外说明

Navicat 远程连接docker容器中的mysql 报错1251 - Client does not support authentication protocol 解决办法

在smarTTY客户端(其它客户端也行)命令行界面进入mysql数据库 (1)容器中登录mysql,进入mysql>命令行   1、docker exec -it mysql01 bash      //mysql01是mysql容器的别名   2、my

额外说明

大数据学习笔记40:Hive - 内置函数(3)

文章目录 一、字符串函数 1、字符串长度函数:length 2、字符串反转函数:reverse 案例:寻找表中回文字符串 3、字符串拼接函数:concat, concat_ws 案例:按指定格式拼接数据 (1)创建数据文件websites.txt (2)

额外说明

从jvm的角度考虑链表是如和存储的,并手写Java单向链表的,问题难在节点和头节点的对象引用

我们都希望手写一个链表算法,但链表的算法有点复杂,尤其是节点的问题,网上也有很多关于链表的操作,但往往是只写出了链表,没有从jvm的角度考虑链表是如和存储的,因而,我今天就我写的链表和大家分享。 1、一个简单的例子 在这个例子中,实用当前类作为自己的属性

额外说明

《Leaflet 进阶知识点》- Leaflet.draw 中英文转换

一、转换 原生是英文,所以要重定义,直接覆盖下面的 leaflet.draw-cn.js 文件。官方说明 L.drawLocal = { draw: { toolbar: { // #TODO: this should be reorgani

ads via 小工具