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

news2024/11/17 15:34:48

博主打算从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和条件变量,我们可以实现线程之间的同步和协作,确保在特定条件满足时执行相应的操作。

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

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

相关文章

商场超市会员日活动怎么群发短信营销

商场超市会员日活动怎么群发短信营销 短信营销的优势 短信营销是一种直接有效的市场推广方式&#xff0c;它具有以下优势&#xff1a; 高开放率&#xff1a;相比于其他营销方式&#xff0c;具有较高的开放率&#xff0c;因为手机用户几乎每天都会查看。 即时性&#xff1a;群…

[单片机课设]十字路口交通灯的设计

题目要求&#xff1a; 模拟交通灯运行情况。南北绿灯亮30秒&#xff0c;南北黄灯亮3秒&#xff0c;东西红灯亮33秒&#xff1b;南北红灯亮33秒&#xff0c;东西绿灯亮30秒&#xff0c;东西黄灯亮3秒&#xff1b;要求数码管同步显示时间的倒计时&#xff0c;用定时器实现延时。…

DELL EMC unity存储系统如何初始化

在客户的存储使用过程中&#xff0c;经常会碰到一些场景需要对存储系统做重新初始化&#xff0c;就是回到出厂时候的配置。比如&#xff0c;客户设备要利旧&#xff0c;二次使用&#xff0c;一般都要回到出厂状态做重新配置的动作。存储严重故障&#xff0c;没有能力修复或者数…

Python AI库pandas读写数据库的应用操作——以sqlite3为例

Python AI库pandas读写数据库的应用操作——以sqlite3为例 本文默认读者具备以下技能&#xff1a; 熟悉python基础知识&#xff0c;vscode或其它编辑工具 已阅读Pandas基础操作文章,了解pandas常见操作 具备自主扩展学习能力 在数据分析和人工智能领域&#xff0c;pandas库和s…

用得助全媒体呼叫中心,让AI落到实处帮品牌做营销

怎么让人工智能落到实处的帮助到我们&#xff1f;我们今天来讲讲中关村科金得助全媒体呼叫中心是怎么让AI帮品牌。 这次聊的案例是知名的护肤品牌&#xff0c;该品牌在中国功能性护肤品市场占有率达到20.5%&#xff0c;这么高的市场占有率客户的咨询量也是非常庞大的&#xff0…

基于C++基础的函数模块

在C中&#xff0c;函数是一段封装了某种功能的代码块&#xff0c;可以在程序的不同地方重复使用。函数定义包含如下组成部分&#xff1a; 函数头&#xff1a;函数头包括函数返回类型、函数名和参数列表。函数返回类型规定了函数返回的数据类型&#xff0c;函数名是函数的唯一标…

【Git】Git在Gitee上的基本操作指南

文章目录 1. 查看 git 版本2. 从Gitee克隆仓库&#xff1a;3. 复制文件到工作目录&#xff1a;4. 将未跟踪的文件添加到暂存区&#xff1a;5. 在本地提交更改&#xff1a;6. 将更改推送到远程仓库&#xff08;Gitee&#xff09;&#xff1a;7. Windows特定提示&#xff1a; 1. …

中仕公考:你的专业在事业编招聘中适合报哪些岗位?

英语专业适合岗位:对外翻译、办公室行政助理、办公室秘书、文化和旅游厅、知识产权局对外纠纷、英语教师等部门 艺术设计适合岗位:电视台、艺术馆、美术馆、博物馆、建筑、市政规划、群艺馆、公共事业管理、水利厅等部门 计算机专业适合岗位:图书馆、气象部门、信息化中心、测…

python代码自动生成器原理 python 生成器原理

python生成器原理剖析 函数的调用满足“后进先出”的原则&#xff0c;也就是说&#xff0c;最后被调用的函数应该第一个返回&#xff0c;函数的递归调用就是一个经典的例子。显然&#xff0c;内存中以“后进先出”"方式处理数据的栈段是最适合用于实现函数调用的载体&…

TMS320F28335学习笔记-时钟系统

第一次使用38225使用了普中的clocksystem例程进行编译&#xff0c;总是编译失败。 问题一&#xff1a;提示找不到文件 因为工程的头文件路径没有包含&#xff0c;下图的路径需要添加自己电脑的路径。 问题二 找不到库文件 例程种的header文件夹和common文件夹不知道从何而来…

Windows下,基于Gradle用Docker发布自己的程序

方案1&#xff1a; windows下打包程序&#xff0c;然后&#xff0c;上传到linux下&#xff0c;生成docker镜像&#xff0c;然后执行。 首先&#xff1a; 由于是采用Gradle管理的项目&#xff0c;打包的时候需要执行build任务。执行完成后&#xff0c;再build\libs目录下应该…

Unity Editor 找物体助手

找啊找朋友~ &#x1f371;功能介绍&#x1f959;使用方法 &#x1f371;功能介绍 &#x1f4a1;输入相关字符串&#xff0c;它会帮你找到名称中带有该字符串的所有物体&#xff0c;还会找包含该字符串的Text、TextMeshProUGUI。 &#x1f959;使用方法 &#x1f4a1;导入插…

【WEB前端2024】开源智体世界:乔布斯3D纪念馆-第17课-跳转用户主页

【WEB前端2024】开源智体世界&#xff1a;乔布斯3D纪念馆-第17课-跳转用户主页 使用dtns.network德塔世界&#xff08;开源的智体世界引擎&#xff09;&#xff0c;策划和设计《乔布斯超大型的开源3D纪念馆》的系列教程。dtns.network是一款主要由JavaScript编写的智体世界引擎…

45天拿下HCIE!誉天云计算 HCIE 3.0 最新通关攻略来啦!

大家好&#xff0c;我是誉天云计算的黄同学。历经4个半月&#xff0c;我成功拿下了自己的云计算HCIE证书。 从学习到备考&#xff0c;再到通过考试拿下证书&#xff0c;这整个过程中我看到了很多&#xff0c;也学到了很多&#xff0c;技术知识和思维能力都受益匪浅。以下是我在…

人工智能_大模型050_模型微调010_模型数据_模型WEB界面代码记录---人工智能工作笔记0185

上面我们把chatglm3的训练代码,和llama2的训练代码,都记录了,然后,再就是: web_demo的代码我们记录一下,然后,我们就继续说下面的内容.至于data目录中的内容,都是一些训练数据,我会 放到资源中,提供下载,这里就不贴出来了,太大了. E:\2024\人工智能\fine-tuning-lab\fine-tun…

RAC GCS_SERVER_PROCESSES参数

参考文档&#xff1a; GCS_SERVER_PROCESSES (oracle.com) 在awr报告中&#xff0c;看到addm建议如下&#xff1a; 其中有Global Cache Messaging和 Global Cache Congestion Activity During the Analysis Period ----------------------------------- Total database tim…

一键接入电商API数据接口1688API通过商品ID、URL采集商品详情页实时数据API接入指南

要一键接入1688电商API数据接口&#xff0c;并通过商品ID或URL采集商品详情页的实时数据&#xff0c;您可以按照以下步骤操作&#xff1a; 注册账号&#xff1a;您需要注册API账号获取必要的API凭证&#xff0c;如Api Key和Api Secret。选择API&#xff1a;根据您的需求&#…

C++学习笔记——对仿函数的理解

文章目录 思维导图仿函数出现的逻辑仿函数使用上的巧妙 仿函数的本质仿函数的优势仿函数语法的巧妙 思维导图 仿函数出现的逻辑 我们在学习stack时会遇到一些新的问题&#xff0c;这些问题需要我们使用非类型模板参数去解决&#xff0c;即我们需要在设计类时需要有一个途径去快…

【MySQL】——函数、存储过程、触发器

&#x1f4bb;博主现有专栏&#xff1a; C51单片机&#xff08;STC89C516&#xff09;&#xff0c;c语言&#xff0c;c&#xff0c;离散数学&#xff0c;算法设计与分析&#xff0c;数据结构&#xff0c;Python&#xff0c;Java基础&#xff0c;MySQL&#xff0c;linux&#xf…

社交新时代:Facebook如何塑造我们的互动方式

在当今社交媒体充斥着人们日常生活的情况下&#xff0c;Facebook作为影响力最大的社交平台之一&#xff0c;已经深深地影响了我们的互动方式和社交行为。从初期的大学校园社交网络发展到如今的全球社交巨头&#xff0c;Facebook已经成为许多人日常生活中不可或缺的组成部分。本…