线程同步:确保多线程程序的安全与高效!

news2025/6/12 17:43:36

全文目录:

    • 开篇语
    • 前序
    • 前言
    • 第一部分:线程同步的概念与问题
      • 1.1 线程同步的概念
      • 1.2 线程同步的问题
      • 1.3 线程同步的解决方案
    • 第二部分:`synchronized`关键字的使用
      • 2.1 使用` synchronized`修饰方法
      • 2.2 使用` synchronized`修饰代码块
    • 第三部分:`ReentrantLock`与条件变量
      • 3.1 `ReentrantLock`的使用
      • 3.2 条件变量:`Condition`
    • 第四部分:死锁的检测与预防
      • 4.1 死锁的概念
      • 4.2 死锁的预防
    • 总结
    • 文末

开篇语

哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云/阿里云/华为云/51CTO;欢迎大家常来逛逛

  今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。

  我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,希望以这种方式帮助到更多的初学者或者想入门的小伙伴们,同时也能对自己的技术进行沉淀,加以复盘,查缺补漏。

小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦。三连即是对作者我写作道路上最好的鼓励与支持!

前序

在多线程编程中,线程同步是确保多个线程在访问共享资源时不会出现竞争问题的关键。线程同步保证了线程之间的协调与数据的一致性,避免了常见的线程安全问题,例如脏数据和竞态条件。随着现代计算机处理能力的提升,多线程编程已经成为开发高效程序的重要技巧。

今天,我们将深入探讨线程同步的基本概念、synchronized关键字的使用、ReentrantLock与条件变量的应用,以及如何检测与预防死锁问题。


前言

在多线程编程中,多个线程可能会同时访问共享资源,如果不加以控制,可能会导致数据的不一致性。例如,一个线程正在修改某个共享变量,另一个线程可能会在这个变量还没完全更新时读取它,导致错误的结果。为了解决这些问题,我们需要使用线程同步技术来确保只有一个线程能够访问共享资源。

今天,我们将通过多个实例深入了解线程同步的概念和工具,帮助你写出更安全、高效的多线程代码。


第一部分:线程同步的概念与问题

1.1 线程同步的概念

线程同步指的是在多线程环境中,确保多个线程在执行过程中能够合理、协调地访问共享资源,从而避免出现线程安全问题。线程同步的目标是确保同一时刻只有一个线程能够访问某个共享资源,这样可以防止数据竞争、死锁等问题。

1.2 线程同步的问题

  • 竞态条件(Race Condition):当两个或多个线程尝试同时访问共享资源,且操作顺序没有得到妥善控制时,就会出现竞态条件,可能导致数据的不一致。

  • 脏数据(Dirty Data):如果一个线程正在修改共享数据,另一个线程读取时没有得到正确的值,就可能读取到脏数据。

  • 死锁(Deadlock):多个线程因相互等待对方持有的资源而进入无限等待的状态,导致程序无法继续执行。

1.3 线程同步的解决方案

为了解决上述问题,我们可以使用不同的线程同步机制,例如:synchronized关键字、ReentrantLockCondition等。这些机制能够确保在同一时刻只有一个线程能够访问共享资源,从而保证数据的一致性。


第二部分:synchronized关键字的使用

synchronized是Java提供的最基础的线程同步工具,它可以修饰方法或代码块,确保同一时刻只有一个线程能够执行被修饰的部分。

2.1 使用 synchronized修饰方法

当一个方法被 synchronized修饰时,表示该方法在执行时会获得该方法所属对象的锁。在多线程环境下,其他线程必须等待当前线程释放锁后才能进入该方法。

示例:

public class SynchronizedExample {
    private int count = 0;

    // 使用synchronized修饰方法
    public synchronized void increment() {
        count++;
    }

    public static void main(String[] args) {
        SynchronizedExample example = new SynchronizedExample();
        
        // 创建多个线程
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                example.increment();
            }
        });

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

        t1.start();
        t2.start();

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Count: " + example.count);  // 输出结果应为2000
    }
}

解释:

  • 在上面的例子中,increment()方法被synchronized修饰,确保在任何时刻只有一个线程可以修改count的值,避免了竞态条件。

2.2 使用 synchronized修饰代码块

如果只需要同步方法中的一部分代码,可以使用synchronized修饰代码块。synchronized代码块的锁是对象锁,而不是方法锁。

示例:

public class SynchronizedBlockExample {
    private int count = 0;

    public void increment() {
        synchronized (this) {  // 锁住当前对象
            count++;
        }
    }

    public static void main(String[] args) {
        SynchronizedBlockExample example = new SynchronizedBlockExample();
        
        // 创建多个线程
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                example.increment();
            }
        });

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

        t1.start();
        t2.start();

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Count: " + example.count);  // 输出结果应为2000
    }
}

解释:

  • synchronized代码块通过锁住this对象,保证只有一个线程能够进入increment()方法中的代码块,避免并发问题。

第三部分:ReentrantLock与条件变量

除了synchronized,Java还提供了更灵活的锁机制——ReentrantLock,它比synchronized提供了更多的功能,特别是在高并发情况下能够提高性能。

3.1 ReentrantLock的使用

ReentrantLockjava.util.concurrent包下的一个锁类,允许显式地获取和释放锁。与synchronized不同,ReentrantLock可以尝试非阻塞式获取锁、可以中断获取锁的线程,还能通过tryLock()方法进行更细粒度的控制。

示例:

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

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

    public void increment() {
        lock.lock();  // 获取锁
        try {
            count++;
        } finally {
            lock.unlock();  // 释放锁
        }
    }

    public static void main(String[] args) {
        ReentrantLockExample example = new ReentrantLockExample();
        
        // 创建多个线程
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                example.increment();
            }
        });

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

        t1.start();
        t2.start();

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Count: " + example.count);  // 输出结果应为2000
    }
}

解释:

  • ReentrantLock可以精确控制锁的获取和释放,相比synchronized,它提供了更好的灵活性和性能。

3.2 条件变量:Condition

Condition接口与Objectwait()notify()类似,但提供了更强大的功能。它通常与ReentrantLock一起使用,可以让线程在某些条件满足时被唤醒。

示例:

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

public class ConditionExample {
    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();
    
    public void produce() throws InterruptedException {
        lock.lock();
        try {
            System.out.println("Producing...");
            condition.await();  // 等待
            System.out.println("Produced!");
        } finally {
            lock.unlock();
        }
    }

    public void consume() throws InterruptedException {
        lock.lock();
        try {
            Thread.sleep(1000);
            System.out.println("Consuming...");
            condition.signal();  // 唤醒等待的线程
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ConditionExample example = new ConditionExample();

        Thread producer = new Thread(() -> {
            try {
                example.produce();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        Thread consumer = new Thread(() -> {
            try {
                example.consume();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        producer.start();
        consumer.start();
    }
}

解释:

  • Condition提供了比wait()notify()更强大的功能,可以在多线程程序中实现更复杂的同步机制。

第四部分:死锁的检测与预防

4.1 死锁的概念

死锁是指两个或多个线程在执行过程中,由于争夺资源而造成一种互相等待的现象,导致程序无法继续执行。

死锁发生的条件:

  1. 互斥条件:每个资源只有一个线程可以使用。
  2. 占有并等待:一个线程占有了某些资源,但在等待其他资源时不释放自己已经占有的资源。
  3. 非抢占条件:资源不能被其他线程强制抢占。
  4. 循环等待:多个线程形成一种环形的等待关系。

4.2 死锁的预防

死锁的预防可以通过以下几种方式:

  1. 避免循环等待:确保线程请求资源的顺序一致。
  2. 避免占有并等待:线程在请求资源时,不持有任何资源。
  3. 使用tryLock()ReentrantLocktryLock()方法可以避免线程死锁。

总结

线程同步是多线程编程中的核心内容,掌握不同的同步机制,能帮助我们避免竞态条件、脏数据和死锁等问题。通过使用synchronized关键字、ReentrantLockCondition等同步工具,我们可以有效地控制线程对共享资源的访问,从而提高程序的安全性和性能。

了解并正确应用这些工具,让你能够编写高效、健壮的并发程序,避免常见的并发问题。在多线程编程中,线程同步不仅是确保程序正常运行的基础,也是提升程序稳定性的关键因素。

… …

文末

好啦,以上就是我这期的全部内容,如果有任何疑问,欢迎下方留言哦,咱们下期见。

… …

学习不分先后,知识不分多少;事无巨细,当以虚心求教;三人行,必有我师焉!!!

wished for you successed !!!


⭐️若喜欢我,就请关注我叭。

⭐️若对您有用,就请点赞叭。
⭐️若有疑问,就请评论留言告诉我叭。


版权声明:本文由作者原创,转载请注明出处,谢谢支持!

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

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

相关文章

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件

今天呢&#xff0c;博主的学习进度也是步入了Java Mybatis 框架&#xff0c;目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学&#xff0c;希望能对大家有所帮助&#xff0c;也特别欢迎大家指点不足之处&#xff0c;小生很乐意接受正确的建议&…

centos 7 部署awstats 网站访问检测

一、基础环境准备&#xff08;两种安装方式都要做&#xff09; bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats&#xff0…

Docker 运行 Kafka 带 SASL 认证教程

Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明&#xff1a;server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)

CSI-2 协议详细解析 (一&#xff09; 1. CSI-2层定义&#xff08;CSI-2 Layer Definitions&#xff09; 分层结构 &#xff1a;CSI-2协议分为6层&#xff1a; 物理层&#xff08;PHY Layer&#xff09; &#xff1a; 定义电气特性、时钟机制和传输介质&#xff08;导线&#…

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容

基于 ​UniApp + WebSocket​实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配​微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)

概述 在 Swift 开发语言中&#xff0c;各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过&#xff0c;在涉及到多个子类派生于基类进行多态模拟的场景下&#xff0c;…

无法与IP建立连接,未能下载VSCode服务器

如题&#xff0c;在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈&#xff0c;发现是VSCode版本自动更新惹的祸&#xff01;&#xff01;&#xff01; 在VSCode的帮助->关于这里发现前几天VSCode自动更新了&#xff0c;我的版本号变成了1.100.3 才导致了远程连接出…

基于Flask实现的医疗保险欺诈识别监测模型

基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施&#xff0c;由雇主和个人按一定比例缴纳保险费&#xff0c;建立社会医疗保险基金&#xff0c;支付雇员医疗费用的一种医疗保险制度&#xff0c; 它是促进社会文明和进步的…

SCAU期末笔记 - 数据分析与数据挖掘题库解析

这门怎么题库答案不全啊日 来简单学一下子来 一、选择题&#xff08;可多选&#xff09; 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘&#xff1a;专注于发现数据中…

vscode(仍待补充)

写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh&#xff1f; debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八

现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet&#xff0c;点击确认后如下提示 最终上报fail 解决方法 内核升级导致&#xff0c;需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)

0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述&#xff0c;后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作&#xff0c;其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…

CMake基础:构建流程详解

目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…

visual studio 2022更改主题为深色

visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中&#xff0c;选择 环境 -> 常规 &#xff0c;将其中的颜色主题改成深色 点击确定&#xff0c;更改完成

关于nvm与node.js

1 安装nvm 安装过程中手动修改 nvm的安装路径&#xff0c; 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解&#xff0c;但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后&#xff0c;通常在该文件中会出现以下配置&…

使用分级同态加密防御梯度泄漏

抽象 联邦学习 &#xff08;FL&#xff09; 支持跨分布式客户端进行协作模型训练&#xff0c;而无需共享原始数据&#xff0c;这使其成为在互联和自动驾驶汽车 &#xff08;CAV&#xff09; 等领域保护隐私的机器学习的一种很有前途的方法。然而&#xff0c;最近的研究表明&…

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端

&#x1f31f; 什么是 MCP&#xff1f; 模型控制协议 (MCP) 是一种创新的协议&#xff0c;旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议&#xff0c;它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版​分享

平时用 iPhone 的时候&#xff0c;难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵&#xff0c;或者买了二手 iPhone 却被原来的 iCloud 账号锁住&#xff0c;这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…

【网络安全产品大调研系列】2. 体验漏洞扫描

前言 2023 年漏洞扫描服务市场规模预计为 3.06&#xff08;十亿美元&#xff09;。漏洞扫描服务市场行业预计将从 2024 年的 3.48&#xff08;十亿美元&#xff09;增长到 2032 年的 9.54&#xff08;十亿美元&#xff09;。预测期内漏洞扫描服务市场 CAGR&#xff08;增长率&…

DAY 47

三、通道注意力 3.1 通道注意力的定义 # 新增&#xff1a;通道注意力模块&#xff08;SE模块&#xff09; class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…