【JAVA进阶篇教学】第十一篇:Java中ReentrantLock锁讲解

博主打算从0-1讲解下java进阶篇教学,今天教学第十篇:Java中ReentrantLock锁讲解。 

在Java并发编程中,保证多线程环境下的数据安全是至关重要的。ReentrantLock 是Java中用于实现线程安全的一种锁机制。本篇博客将深入介绍 ReentrantLock 的原理、详细说明,并通过案例演示线程不安全情况以及如何使用 ReentrantLock 实现线程安全。

目录

一、原理 

二、代码测试

1.线程不安全案例

2.线程安全案例

三、公平性 

四、条件变量


一、原理 

ReentrantLock 是Java中的一种锁实现,它具有可重入性,即同一线程可以多次获取同一把锁而不会出现死锁。它使用了一种互斥锁的机制,确保了在同一时刻只有一个线程可以访问被锁定的代码块或方法。

ReentrantLock是 Java 中的一个可重入锁类,它实现了Lock接口。ReentrantLock的原理主要涉及以下几个方面:

  1. 锁状态:ReentrantLock通过一个内部的锁状态来表示当前锁的占用情况。锁状态可以是未锁定、锁定和重入锁定等状态。
  2. 获取锁:当线程调用lock方法时,它会尝试获取锁。如果锁当前没有被其他线程占用,那么该线程将成功获取锁,并将锁状态设置为锁定。如果锁已经被其他线程占用,那么当前线程将被阻塞,直到锁被释放。
  3. 释放锁:当线程调用unlock方法时,它会释放锁。如果当前线程持有锁,那么它将把锁状态设置为未锁定,并唤醒等待获取锁的线程。
  4. 可重入性:ReentrantLock支持可重入性,即同一个线程可以多次获取同一个锁。在获取锁时,锁的持有计数会增加,在释放锁时,锁的持有计数会减少。只有当锁的持有计数为 0 时,锁才会被完全释放。
  5. 公平性:ReentrantLock可以选择是否采用公平锁策略。公平锁保证线程按照先来先服务的顺序获取锁,而不公平锁则允许线程抢占锁。
  6. 条件变量:ReentrantLock还提供了条件变量的支持,可以用于实现线程的等待和通知机制。线程可以在满足特定条件时等待,直到其他线程通知条件满足。

二、代码测试

1.线程不安全案例

public class UnsafeCounter {
    private int count = 0;

    public void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }

    public static void main(String[] args) {
        UnsafeCounter counter = new UnsafeCounter();
        // 创建两个线程并发增加计数
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });

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

        // 等待两个线程执行完成
        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 输出最终计数值
        System.out.println("Count: " + counter.getCount()); // 预期结果: 可能小于 2000
    }
}

在这个示例中,由于 increment() 方法没有同步控制,两个线程同时对 count 进行增加操作,可能导致计数不准确。得到的结果偶尔可能是正确的2000。

2.线程安全案例

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SafeCounter {
    private int count = 0;
    private Lock lock = new ReentrantLock();

    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }

    public int getCount() {
        lock.lock();
        try {
            return count;
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        SafeCounter counter = new SafeCounter();
        // 创建两个线程并发增加计数
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });

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

        // 等待两个线程执行完成
        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 输出最终计数值
        System.out.println("Count: " + counter.getCount()); // 预期结果: 可能小于 2000
    }
}

在这个示例中,使用 ReentrantLock 来确保了对 count 的操作是线程安全的,保证了最终输出的计数值是准确的。

三、公平性 

公平性是指在多线程环境下,锁的获取顺序应该遵循先来先服务的原则,即先请求锁的线程应该先获得锁。在 Java 中,可以通过设置ReentrantLock的构造函数参数来选择使用公平锁或非公平锁。以下是一个使用公平锁的示例代码:

public class ReentrantLockTest extends Thread {
    private static ReentrantLock lock = new ReentrantLock(true); // 创建公平锁

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            lock.lock(); // 获取锁
            try {
                System.out.println(Thread.currentThread().getName() + " 获得锁");
                // 执行临界区操作
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock(); // 释放锁
            }
        }
    }

    public static void main(String[] args) {
        ReentrantLockTest thread1 = new ReentrantLockTest();
        ReentrantLockTest thread2 = new ReentrantLockTest();
        ReentrantLockTest thread3 = new ReentrantLockTest();

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

默认情况下非公平锁

Lock lock = new ReentrantLock();

创建公平锁

Lock fairLock = new ReentrantLock(true);

四、条件变量

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class ConditionVariableExample {
    private ReentrantLock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public void waitForCondition() {
        try {
            lock.lock();
            System.out.println("线程等待条件满足...");
            condition.await();
            System.out.println("线程收到通知,条件满足!");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void notifyCondition() {
        try {
            lock.lock();
            System.out.println("通知线程,条件满足...");
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        ConditionVariableExample example = new ConditionVariableExample();

        // 创建并启动等待条件的线程
        Thread waitingThread = new Thread(() -> {
            example.waitForCondition();
        });
        waitingThread.start();

        // 模拟条件满足的情况
        // 可以在其他地方执行此操作,以通知等待线程条件已满足
        example.notifyCondition();
    }
}

在上述示例中,我们创建了一个ReentrantLock对象lock和一个与之关联的条件变量condition。

  1. waitForCondition方法用于线程等待条件满足。在方法内部,首先获取锁,然后打印出等待消息,并使用condition.await方法使线程等待条件满足。在等待过程中,线程会释放锁,并进入阻塞状态。
  2. notifyCondition方法用于通知等待条件的线程。在方法内部,获取锁后,打印出通知消息,并使用condition.signalAll方法通知所有等待条件的线程。

在main方法中,我们创建了一个等待条件的线程waitingThread,并启动它。然后,模拟条件满足的情况,调用notifyCondition方法通知等待线程。
通过使用ReentrantLock和条件变量,我们可以实现线程之间的同步和协作,确保在特定条件满足时执行相应的操作。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MapReduce代码

WordCount 数据准备&#xff1a; a.txt lxy lxy lxy zhang wsoossj liagn guui liang liagn代码&#xff08;在idea中创建一个Maven工程&#xff09;&#xff1a; mapper&#xff1a; package com.lxy.mr.wordcount.thi;import org.apache.hadoop.io.LongWritable; import…

ABAP ALSM_EXCEL_TO_INTERNAL_TABLE 导入Excel的几个问题

1、没有办法多页签 2、单元格50个字符限制&#xff0c;每个单元格仅读取50个字符 3、Excel单元格总不能有不可见字符换 eg 回车&#xff0c;换行 # 等否则读取的结果会加上引号

【会议征稿】第四届机器学习与智能系统工程国际学术会议(MLISE 2024, 6/28-30)

第四届机器学习与智能系统工程国际学术会议&#xff08;MLISE 2024)由珠海科技学院主办&#xff0c;并获得多家高校与研究机构共同支持&#xff0c;将于204年6月28-30日在珠海召开。 MLISE将围绕 “机器学习与智能系统工程” 相关领域&#xff0c;为国内外研究人员与学者提供一…

kafka学习笔记04(小滴课堂)

Kafka的producer生产者发送到Broker分区策略讲解 Kafka核心API模块-producer API讲解实战 代码&#xff1a; ProducerRecord介绍和key的作用 Kafka核心API模块-producerAPI回调函数实战 producer生产者发送指定分区实战 我们设置5个分区。 我们指定分区。 重新指定一个分区&am…

根据后端返回下拉请求地址,前端动态请求拿到下拉数据渲染

完整代码如下&#xff1a; <template> <!-- 资源列表页 --> <div> <div> <i click"$router.go(-1)" style" color: #409eff; cursor: pointer; margin-right: 5px; font-size: 18px; " class"el-icon-back" ><…

【算法】二分查找——二分查找

本节博客详述“二分查找”并且以例子来进行讨论&#xff0c;有需要借鉴即可。 目录 1.二分查找1.1使用前提1.2模板 2.题目3.题解代码示例4.二分查找的一般模板5.总结 1.二分查找 1.1使用前提 使用的条件&#xff1a;数组具有“二段性”&#xff0c;二段性指的是数组可以根据某…

数据库系统概论(个人笔记)(第二部分)

数据库系统概论&#xff08;个人笔记&#xff09; 文章目录 数据库系统概论&#xff08;个人笔记&#xff09;2、关系模型简介2.1 关系数据库的结构2.2 数据库模式2.3 键2.4 模式图2.5 关系查询语言2.6 关系代数 2、关系模型简介 2.1 关系数据库的结构 Structure of Relational…

网络2--MAC地址,IP地址的理解

引入&#xff1a; 每一张主机都会有一张网卡&#xff0c;每一张网卡都有一个48bit位的序列号 当我们的热点被连上&#xff0c;你查看时&#xff0c;就会出现MAC地址&#xff0c;IP地址 那么他们两个是什么呢&#xff1f;&#xff1f;&#xff1f; MAC地址 在同一个局域网中…

【C++】每日一题 17 电话号码的字母组合

给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 可以使用回溯法来解决这个问题。首先定义一个映射关系将数字与字母对应起来…

在js中table表格中进行渲染轮播图

效果图&#xff1a;示例&#xff1a; <!DOCTYPE html> <html> <head><meta charset"utf-8"><title></title><script src"js/jquery-3.6.3.js"></script><style>/* 轮播图 */.basko {width: 100%;h…

51单片机小车制造过程记录

首先感谢B站up主好家伙vcc的资料。 这次小车做出来虽然资料挺全的&#xff0c;但中间还是犯了很多不该犯的错误。 第一个&#xff0c;物料这次我们搞错了挺多&#xff0c;最离谱的应该是最小系统板都错了。 资料里用的stm32f103c8t6&#xff0c;我们开始买成了stm32f103c8t6。…

Qt学习笔记1.3.4 QtCore-Qt资源系统

文章目录 资源收集文件(.qrc)外部二进制资源内编译(compiled-in)资源压缩使用应用程序中的资源使用库中的资源 Qt资源系统是一种 独立于平台的机制&#xff0c;用于在应用程序的可执行文件中存储二进制文件。如果您的应用程序总是需要一组特定的文件(图标、翻译文件等)&#x…

信息与未来2017真题笔记

T1. 龟兔赛跑 题目描述 兔子又来找乌龟赛跑啦&#xff01;同样的错误兔子不会犯两次&#xff0c;所以兔子提出赛跑的时候&#xff0c;乌龟就觉得这场比赛很不公平。于是兔子进一步放宽了条件&#xff0c;表示他可以在比赛开始以后先睡 t t t 分钟再开始追乌龟。 乌龟这下没…

java+jsp+sql server 医院住院管理系统论文(二)

⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️ ➡️点击免费下载全套资料:源码、数据库、部署教程、论文、答辩ppt一条龙服务 ➡️有部署问题可私信联系 ⬆️⬆️⬆️​​​​​​​⬆️…

TikTok起号的八大技巧分享

国内的传统生意都是可以在抖音上做&#xff0c;那么也可以在TikTok 上重新做一遍。那该如何才能把握住这片巨大的蓝海&#xff0c;TikTok 账号的运营就成为了主要的关键了&#xff0c;对于TikTok账号运营的八大秘籍&#xff0c;大家一起看看是如何做的&#xff1f; 一、固定节…

机器视觉运动控制一体机在点胶胶路检测上的应用

市场应用背景 点胶通过使用不同类型的粘合剂&#xff0c;实现产品的密封、绝缘、导热和耐腐蚀等作用&#xff0c;广泛应用于各种产品的制造。在点胶加工生产中&#xff0c;通过检测胶水的宽度、点胶位置和胶路连续性等&#xff0c;可确保产品性能的可靠性和稳定性。 在现实生…

骨传导耳机哪个品牌值得入手?盘点5款高人气热门机型推荐!

随着人们对健康生活方式的追求和户外运动的普及&#xff0c;骨传导耳机的需求也日益增长。然而&#xff0c;随着骨传导耳机的热度增加&#xff0c;市场上也开始出现一些低质量的骨传导耳机产品&#xff0c;这些劣质耳机在音质、佩戴舒适度或安全性上存在各种不足&#xff0c;甚…

Dubbo全局处理业务异常 (自定义dubbo异常过滤器)

自定义dubbo异常过滤器 一、前置问题介绍&#xff1a;问题一问题二 二、Dubbo的异常过滤器源码如下&#xff1a;三、实现方案 - 重写Dubbo的Filter异常过滤器至此&#xff0c;Dubbo自定义异常过滤器已完结&#xff01; 一、前置问题介绍&#xff1a; 问题一 在dubbo框架中&am…

多臂老虎机

多臂老虎机 有n根拉杆的的老虎机&#xff0c;每根拉杆获得奖励(值为1)的概率各不相同。 期望奖励更新 Q k 1 k ∑ i 1 k r i 1 k ( r k ∑ i 1 k − 1 r i ) 1 k ( r k k Q k − 1 − Q k − 1 ) Q k − 1 1 k [ r k − Q k − 1 ] Q_k\frac 1k \sum^{k}_{i1}r_i\\…

oracle10g dbca和netca报错

oracle10g dbca和netca报错 [oraclecqnew database]$ netcaOracle Net Services Configuration: Warning: Cannot convert string "-b&h-lucida-medium-r-normal-sans-*-140-*-*-p-*-iso8859-1" to type FontStruct Configuring Listener:LISTENER不影响使用&am…