并发编程-ReentrentLock概念及使用

news2024/10/10 22:01:06

1.ReentrantLock概念

ReentrantLock 是 Java 中的一种可重入锁,属于 java.util.concurrent.locks 包。它提供了比 synchronized 关键字更灵活的锁机制,允许更复杂的线程同步控制。以下是 ReentrantLock 的一些关键概念和特点:

  • 可重入性:同一个线程可以多次获得同一把锁,而不会导致死锁。这意味着如果一个线程已经持有了锁,它可以再次获得该锁而不被阻塞。

  • 显式锁定:与 synchronized 不同,ReentrantLock 需要手动锁定和解锁。使用 lock() 方法来锁定,使用 unlock() 方法来解锁。

  • 公平锁与非公平锁ReentrantLock 可以选择是否使用公平性策略。公平锁按照请求锁的顺序分配锁,而非公平锁则不保证顺序,可能会导致某些线程饥饿。

  • 条件变量ReentrantLock 允许使用条件变量(Condition),可以实现更复杂的线程通信机制。通过 newCondition() 方法创建条件变量,可以使用 await()signal() 等方法进行线程之间的协调。

  • 性能:在某些情况下,ReentrantLock 的性能可能优于 synchronized,尤其是在高竞争的场景中,因为它采用了更复杂的算法来管理锁的获取和释放(可以进行更低粒度锁的控制)。

2.基本使用Demo

2.1 lock、unlock方法-Demo

public class ReentrantLockDemo01 {
    public static void main(String[] args) throws InterruptedException {
        ReentrantLock reentrantLock = new ReentrantLock();// 默认非公平锁
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                reentrantLock.lock();
                System.out.println(Thread.currentThread().getName() + " 成功获取锁");
                try {
                    System.out.println(Thread.currentThread().getName() + ".....");
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                reentrantLock.unlock();
                System.out.println(Thread.currentThread().getName() + " 释放锁");
            }
        });

        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);// 确保thread1先执行,thread1进行锁的获取和释放
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println(Thread.currentThread().getName() + " 尝试获取锁");
                reentrantLock.lock();
                System.out.println(Thread.currentThread().getName() + " 成功获取锁");
                try {
                    Thread.sleep(2000);
                    System.out.println(Thread.currentThread().getName() + ".....");
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println(Thread.currentThread().getName() + " 释放锁");
            }
        });

        thread1.start();
        thread2.start();

        thread1.join();
        thread2.join();
        System.out.println(Thread.currentThread().getName() + " 执行了");
    }
}

输出:

Thread-0首先获取锁,然后模拟执行2s业务,之后释放锁;

Thread-1首先睡眠1s,然后获取锁,但是被阻塞;等Thread-0释放锁后,Thread-1成功获取锁,执行业务逻辑。

 2.2 可重入性-Demo

public class ReentrantLockDemo02 {
    public static void main(String[] args) throws InterruptedException {
        ReentrantLock reentrantLock = new ReentrantLock();// 默认非公平锁
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                reentrantLock.lock();
                try{
                    System.out.println(Thread.currentThread().getName() + " start");

                    reentrantLock.lock();// 可重入
                    System.out.println();
                    System.out.println(Thread.currentThread().getName() + " reStart");

                    System.out.println(Thread.currentThread().getName() + " end");
                }finally {
                    reentrantLock.unlock();
                }

            }
        });
        
        thread1.start();

        thread1.join();
        System.out.println(Thread.currentThread().getName() + " 执行了");
    }
}

输出:

2.3 公平及非公平锁

默认为非公平锁(ReentrentLock的构成方法)

2.4 可中断性-Demo

public class ReentrantLockWithInterruptorDemo {
    public static void main(String[] args) {
        ReentrantLock reentrantLock = new ReentrantLock();

        // t1先获取锁
        Thread t1 = new Thread(()->{
            try {
                System.out.println(Thread.currentThread().getName() + " 启动了");
                reentrantLock.lock();
                System.out.println(Thread.currentThread().getName() + " 获取到锁");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    System.out.println("t1报错");
                    throw new RuntimeException(e);
                }
            } finally {
                reentrantLock.unlock();
            }
            System.out.println(Thread.currentThread().getName() + " 释放锁");
        });

        t1.start();

        // 测试重点: t2后获取锁。测试lock、lockInterruptibly方法
        Thread t2 = new Thread(()->{
            try {
                System.out.println(Thread.currentThread().getName() + " 启动了");
                // 确保t2先执行性
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                System.out.println("t2报错");
                throw new RuntimeException(e);
            }
            try {
                long l = System.currentTimeMillis();
                // reentrantLock.lock();
                reentrantLock.lockInterruptibly();
                long l1 = System.currentTimeMillis();
                System.out.println(Thread.currentThread().getName() + " 等待获取锁的时间"+(l1-l));
            } catch (InterruptedException e) {
                System.out.println(Thread.currentThread().getName() + " 获取锁失败,t2需要中断,t2要退出啦");
                throw new RuntimeException(e);
            } finally {
                reentrantLock.unlock();
            }
        });
        t2.start();

        Thread t3 = new Thread(()->{
            try {
                System.out.println(Thread.currentThread().getName() + " 启动了");
                Thread.sleep(2000);
            } catch (Exception e) {
                System.out.println(Thread.currentThread().getName() + " 报错");
                throw new RuntimeException(e);
            }
            t2.interrupt();// 中断t2
            System.out.println("t3中断t2了");
        });
        t3.start();
    }
}

输出:在Thread-1中,调用reentrantLock.lockInterruptibly();,lockInterruptibly表示该线程如果获取锁失败,则接收中断。在Thread-2中,主动中断Thread-1,所以Thread-1报出异常。

2.5 Condition条件变量-Demo

ReentrantLock 允许使用条件变量(Condition),可以实现更复杂的线程通信机制。通过 newCondition() 方法创建条件变量,可以使用 await()signal() 等方法进行线程之间的协调。

通过Condition模拟实现生产者-消费者模型:

仓库类:

/**
 * 仓库类
 */
public class Depot {
    private int capacity;    // 仓库的容量
    private int size;        // 仓库的实际数量
    private ReentrantLock lock;        // 可重入锁
    private Condition fullCondtion;    // 生产条件
    private Condition emptyCondtion;   // 消费条件

    /**
     * 初始化
     */
    public Depot() {
        capacity = 100;
        size = 0;
        lock = new ReentrantLock();
        fullCondtion = lock.newCondition();
        emptyCondtion = lock.newCondition();
    }

    /**
     * 生产流程
     * @param val:生产的个数
     */
    public void produce(int val) {
        try {
            lock.lock();
            System.out.println("生产者:" + Thread.currentThread().getName() + "开始执行");
            //left:还需生产的个数
            int left = val;
            while (left > 0) {
                //当仓库满了,需要暂停生产
                if (size >= capacity) {
                    System.out.println(Thread.currentThread().getName() + ":当前仓库已满,生产进程进入等待");
                    //当前生产进程阻塞
                    fullCondtion.await();
                }
                // incr:此次生产的数量
                int incr = (capacity - size) > left ? left : (capacity - size);
                size = size + incr;
                left = left - incr;
                System.out.println(Thread.currentThread().getName() + "生产:incr=" + incr + ",left=" + left + ",size=" + size);
                // 生产完了,唤起消费进程
                emptyCondtion.signal();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //释放锁
            lock.unlock();
        }
    }

    /**
     * 消费流程
     *
     * @param val:消费的个数
     */
    public void consume(int val) {
        try {
            lock.lock();
            System.out.println("消费者:" + Thread.currentThread().getName() + "开始执行");
            //left:剩余消费的数量
            int left = val;
            while (left > 0) {
                if (size <= 0) {
                    System.out.println(Thread.currentThread().getName() + ":当前仓库已空,消费进程进入等待");
                    emptyCondtion.await();
                }
                // dec : 此次消费的个数
                int dec = size > left ? left : size;
                left = left - dec;
                size = size - dec;
                System.out.println(Thread.currentThread().getName() + "消费:dec=" + dec + ",left=" + left + ",size=" + size);
                // 唤醒生产进程
                fullCondtion.signal();
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

 生产者:

/**
 * 生产者
 */
public class Producer {
    private Depot depot;

    public Producer(Depot depot){
        this.depot= depot;
    }

    public void produce(int val){
        new Thread(() -> {
            depot.produce(val);
        }).start();
    }
}

消费者:

/**
 * 消费者
 */
public class Consumer {
    private Depot depot;

    public Consumer(Depot depot){
        this.depot = depot;
    }

    public void consume(int val){
        new Thread(){
            @Override
            public void run() {
               depot.consume(val);
            }
        }.start();
    }
}

测试类:

public class Main {
    public static void main(String[] args) {
        Depot depot = new Depot();
        Producer producer = new Producer(depot);
        Consumer consumer = new Consumer(depot);

        consumer.consume(150);
        producer.produce(200);
        producer.produce(100);
        consumer.consume(150);
        consumer.consume(50);
        producer.produce(200);
    }
}

结果输出:

 说明:在仓库类中定义生产者和消费者共用的ReentrentLock及Condition。当调用消费者的时候,首先竞争Lock,然后调用Condition.await方法竞争Condition资源,进行消费,当仓库产品为空时,唤醒生产者,消费者释放锁。生产者首先竞争Lock,然后调用Condition.await()方法竞争,开始生产产品,当仓库为满时,通过Condition.signal()唤醒消费者进行消费。

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

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

相关文章

Linux的Redis安装部署

Redis是一个nosql数据库,速度快,key-value型数据库 1.root用户执行 yum install -y epel-release 配置epel仓库 2.安装Redis 命令: yum install -y redis 3.启动服务: systemctl start redis 4.注意关闭firewalld防火墙, 5.进入Redis服务端redis-cli

tensorflow入门案例手写数字识别人工智能界的helloworld项目落地1

参考 https://tensorflow.google.cn/?hlzh-cn https://tensorflow.google.cn/tutorials/keras/classification?hlzh-cn 项目资源 https://download.csdn.net/download/AnalogElectronic/89872174 文章目录 一、案例学习1、导入测试和训练数据集&#xff0c;定义模型&#xff…

el-tree 修改每个层级的背景色

目录 一、思路 二、代码 1. HTML部分 2. js部分 3. css部分 案例图 一、思路 使用 render-content 插槽来自定义节点内容。根据节点的层级动态添加 CSS 类。写一个方法&#xff0c;用于&#xff1a;递归地获取节点的层级。如果节点没有父节点&#xff0c;则返回当前层级…

嵌入式工业显示器在食品生产行业的应用

嵌入式工业显示器在食品生产行业的应用主要体现在以下几个方面&#xff1a; 一、自动化控制与精准监测 嵌入式工业显示器通常与各类传感器和执行器集成&#xff0c;能够实时显示生产线的运行状态&#xff0c;实现自动化控制和精准监测。在食品生产过程中&#xff0c;从原材料…

Vscode+Pycharm+Vue.js+WEUI+django火锅(三)理解Vue

新创建的Vue项目里面很多文件&#xff0c;对于新手&#xff0c;老老实实做一下了解。 1.框架逻辑 框架的逻辑都是相通的&#xff0c;花点时间理一下就清晰了。 2.文件目录及文件 创建好的vue项目下&#xff0c;主要的文件和文件夹要先认识一下&#xff0c;并与框架逻辑对应起…

Centos7 搭建单机elasticsearch

以下是在 CentOS 7 上安装 Elasticsearch 7.17.7 的完整步骤&#xff1a;&#xff08;数据默认保存在/var/lib/elasticsearch下&#xff0c;自行更改&#xff09; 一、装 Java 环境 Elasticsearch 是用 Java 编写的&#xff0c;所以需要先安装 Java 运行环境。 检查系统中是…

springboot酒店客房管理系统-计算机毕业设计源码43070

目录 摘要 1 绪论 1.1 选题背景与意义 1.2国内外研究现状 1.3论文结构与章节安排 2系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1系统开发流程 2.2.2 用户登录流程 2.2.3 系统操作流程 2.2.4 添加信息流程 2.2.5 修改信息流程 2.2.6 删除信息流程 2.3 系统功能…

模拟堆算法

题目 代码 #include <bits/stdc.h> using namespace std; const int N 1e510; int heap[N], sz, cnt; int th[N], ht[N]; void hswap(int a, int b) {swap(heap[a], heap[b]);swap(ht[a], ht[b]);swap(th[ht[a]], th[ht[b]]); } void down(int h) {int t h;if(2*h <…

【开源项目】Jsoncpp的简单使用

Jsoncpp是一个开源项目&#xff0c;它是一个用于处理JSON&#xff08;JavaScript Object Notation&#xff09;数据的C库。它支持将C结构化的数据转化为JSON字符串&#xff0c;也支持将JSON字符串转化为结构化数据 JSON&#xff08;JavaScript Object Notation&#xff09;数据…

质量好的宠物空气净化器分享,希喂、小米、范罗士性能大揭秘

双十一大家都打算买什么&#xff1f;国庆小长假刚结束&#xff0c;没想到下周就开始预售付定金了。看了一圈自己没有什么想买的&#xff0c;就打算给我家毛孩子买点什么。之前一直很纠结要不要买宠物空气净化器&#xff0c;觉得有点贵迟迟没入手&#xff0c;去看了眼&#xff0…

直线导轨在自动化设备中需要注意什么?

直线导轨属于精密传动配件&#xff0c;因而在使用时要求有相当地慎重态度&#xff0c;如果使用不当&#xff0c;也不能达到预期的性能效果&#xff0c;尤其是保管和保养不当&#xff0c;很容易造成导轨失效等问题&#xff0c;导致无法正常使用。因此&#xff0c;自动化设备中使…

UI设计岗前训练

UI设计&#xff0c;全称User Interface Design&#xff0c;即用户界面设计&#xff0c;是指对软件或应用程序的人机交互、操作逻辑、界面美观的整体设计。它涉及软件的操作流程、逻辑、布局、色彩、字体、图标、按钮、动画效果等多个方面&#xff0c;旨在提升用户体验和满意度。…

又被Transformer秀到了!结合小样本学习发A会!

在有限的数据资源下&#xff0c;为了训练出高性能的机器学习模型&#xff0c;我们常会考虑Transformer小样本学习。 这是因为Transformer能从有限的数据中提取更多有用的信息&#xff0c;这样与小样本学习结合&#xff0c;可以更有效的帮助我们提高模型的性能&#xff0c;加速…

C9800的Flex配置

C9800的配置和AireOS WLC的配置架构有了很大的调整&#xff0c;在配置一个WLAN的时候&#xff0c;可能相对麻烦一些&#xff0c;但是架构还是比较清晰。 这里记录一下针对Flex的配置&#xff0c;主要分为如下几个摘要&#xff08;以及对应的CLI配置&#xff09;&#xff1a; 1…

python+request+unittest+ddt自动化框架

参考资料&#xff1a; 用户中心 - 博客园 抓包模拟器笔记 肖sir__接口自动化pythonrequestddt&#xff08;模版&#xff09; - xiaolehua - 博客园 pythonrequestunittestddt自动化框架 博文阅读密码验证 - 博客园 肖sir__python之模块configparser - xiaolehua - 博客园 c…

4G路由网关R10在智能制造生产线的应用

在当今智能制造的时代&#xff0c;高效稳定的网络连接和数据传输至关重要。4G 路由网关 R10 以其卓越的性能&#xff0c;在智能制造生产线中发挥着重要作用。 4G 路由网关 R10 是一款功能强大的网络设备。它支持多种网络连接方式&#xff0c;包括 4G 网络、有线网络等&#xff…

骨传导耳机哪个牌子好?五大高热度骨传导耳机测评推荐

随着科技的不断进步&#xff0c;耳机已经成为了我们日常生活中不可或缺的一部分。从传统的有线耳机到无线蓝牙耳机&#xff0c;再到现在的骨传导耳机&#xff0c;每一次技术的革新都给用户带来了全新的体验。骨传导耳机&#xff0c;作为一种新兴的耳机类型&#xff0c;它通过颅…

乐歌E5,E6系列升降桌质量如何?2024推荐必买的四款热销型号

在数字化时代&#xff0c;电脑桌成为了我们日常生活和工作中不可或缺的一部分。然而&#xff0c;长时间坐在固定高度的电脑桌前&#xff0c;不仅会影响我们的工作效率&#xff0c;还可能对身体健康造成不良影响。因此&#xff0c;一款能够电动升降的电脑桌显得尤为重要。 乐歌…

Linux-创建函数

基本的脚本函数 返回值 在函数中使用变量 数组变量和函数-向函数传递数组参数 数组变量和函数-从函数返回数组 函数递归 创建库 在命令行上使用函数-在命令行上创建函数 在命令行上使用函数-在.bashrc文件中定义函数

计算机组成的抽象以及相关技术

第1章 计算机抽象以及相关技术 最高端的服务器是超级计算机&#xff1a;几十万个处理器和太字节(terabyte)2的40次方的内存TB&#xff0c;成本数千万或数亿。通常写为1000000000000&#xff0c;万亿。 云计算接替了传统服务器称为仓储级计算机中心&#xff0c;可以实现Saas(软…