”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 多线程:工程师的关键概念 - 第 1 部分

多线程:工程师的关键概念 - 第 1 部分

发布于2024-11-08
浏览:343

Multithreading : Key Concepts for Engineers - Part 1

了解关键的多线程概念对于软件开发人员至关重要,因为它不仅可以增强技能,还可以直接影响应用程序开发、可扩展性和软件解决方案的整体质量。

原子性

在多线程上下文中,原子操作确保一个线程可以执行一系列操作而不会被其他线程中断。多个线程可能会尝试同时读取或写入共享数据。如果没有原子性,并发修改可能会导致不一致或意外的结果,通常称为竞争条件。

Java 规范保证“读”和“写”是原子操作而不是它们的组合。因此,根据规范,“读取、加 1,然后将结果写回”的操作不是原子操作。此类操作称为复合操作,在我们的代码中使用它们时,它们通常需要是原子的。

原子操作示例:

  1. 递增计数器:如果两个线程在没有原子性的情况下同时递增计数器,则它们可能都读取相同的值并写回相同的递增值,从而导致丢失一个增量。

  2. 更新共享变量:如果一个线程正在读取一个值,而另一个线程正在修改它,如果没有原子性,读取线程可能会得到不一致的值。

实现原子性:

  • 原子类:许多编程语言提供原子类(例如 Java 中的 AtomicInteger),封装保证原子性的操作。

  • 同步方法/块:在Java等语言中,可以使用synchronized关键字来确保一次只有一个线程可以执行一段代码或一个方法。

  • :使用显式锁(例如 Java 中的 ReentrantLock)来管理对共享资源的访问。

好处

  • 性能:java.util.concurrent.atomic中的类还提供了无锁的方式来保证线程安全,使其成为很多场景下的首选。
  • 简单性:使用原子类可以简化代码,因为开发人员不需要管理锁,可以专注于程序的逻辑。
  • 线程安全:原子操作确保跨多个线程安全地更新变量,而不存在数据损坏或竞争条件的风险。

不变性

不可变性是指对象在创建后其状态不能被修改的属性。在编程中,不可变对象是那些一旦初始化就不能更改或改变的对象。不是修改不可变的对象,而是创建一个具有所需更改的新对象。

不可变意味着一旦对象的构造函数完成执行,该实例就无法更改。

不可变对象的特性

  • 无状态变化: 一旦创建了不可变对象,其状态(属性或字段)在其整个生命周期中保持不变。

  • 线程安全: 不可变对象可以在多个线程之间安全共享,无需同步,因为它们无法修改。

  • 哈希码稳定性: 不可变对象的哈希码在其整个生命周期中保持不变,使其适合在 HashMap 或 HashSet 等基于哈希的集合中使用。

实现不变性:

  • 记录的使用(Java 14 中): 在 Java 中,记录功能提供了一种创建不可变数据类的简洁方法。
public record ImmutablePoint(int x, int y) {}
  • 使用不可变数据结构:利用编程语言或库提供的现有不可变数据结构,例如:
  1. Java: Collections.unmodifyingList(), List.of(), Set.of()

  2. C#: System.Collections.Immutable 中的 ImmutableList、ImmutableArray

  3. Python: 元组本质上是不可变的。

  • 使用 Final 字段: 将类的字段声明为 Final。这确保了在对象构造期间只能分配字段一次。

  • 无 Setter: 避免为可变字段提供 setter 方法。这可以防止外部代码在构造对象后更改对象的状态。

public final class ImmutablePoint {
    private final int x;
    private final int y;

    public ImmutablePoint(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }
}
  • 静态工厂方法:不提供公共构造函数,而是使用返回对象新实例的静态工厂方法,明确状态无法更改

  • 构建器模式(对于复杂对象): 对于需要很多参数的对象,使用构建器模式创建不可变对象。构建器累积参数并在最后构造一个不可变的实例。

好处

  • 并发: 如果不可变对象的内部结构有效,则它始终有效。不同的线程不可能在该对象内创建无效状态。因此,不可变对象是线程安全的。

  • 垃圾收集:垃圾收集器对不可变对象做出逻辑决策要容易得多。

尾奏

用这些知识武装自己不仅可以增强您编写高性能代码的能力,还可以让您为应对现代软件开发的挑战做好准备,在现代软件开发中,响应能力和可扩展性至关重要。当您继续进入多线程世界时,请记住,您掌握的每个概念都将有助于您作为开发人员的成长以及您创建满足并超越用户期望的应用程序的能力。

请继续关注,我们将在接下来的文章中重点讨论饥饿、死锁、竞争条件、操作系统调度等问题,这将提高您的编程技能并促进您的职业生涯!

参考

非常感谢在线文档、社区和所有可用资源,使本文成为可能。

  1. 信息图
  2. 了解基本的多线程概念
  3. 原子性
  4. 什么是不可变的

免责声明:本文由人工智能辅助。文章结构和想法列表是 100% 手动策划和研究的。我校对了所有人工智能生成的文本,以确保信息准确性并添加一些上下文

版本声明 本文转载于:https://dev.to/anwaar/multithreading-key-concepts-for-engineers-part-1-4g73?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 如何实时捕获和流媒体以进行聊天机器人命令执行?
    如何实时捕获和流媒体以进行聊天机器人命令执行?
    在开发能够执行命令的chatbots的领域中,实时从命令执行实时捕获Stdout,一个常见的需求是能够检索和显示标准输出(stdout)在cath cath cant cant cant cant cant cant cant cant interfaces in Chate cant inter...
    编程 发布于2025-04-08
  • 找到最大计数时,如何解决mySQL中的“组函数\”错误的“无效使用”?
    找到最大计数时,如何解决mySQL中的“组函数\”错误的“无效使用”?
    如何在mySQL中使用mySql 检索最大计数,您可能会遇到一个问题,您可能会在尝试使用以下命令:理解错误正确找到由名称列分组的值的最大计数,请使用以下修改后的查询: 计数(*)为c 来自EMP1 按名称组 c desc订购 限制1 查询说明 select语句提取名称列和每个名称...
    编程 发布于2025-04-08
  • 为什么我会收到MySQL错误#1089:错误的前缀密钥?
    为什么我会收到MySQL错误#1089:错误的前缀密钥?
    mySQL错误#1089:错误的前缀键错误descript [#1089-不正确的前缀键在尝试在表中创建一个prefix键时会出现。前缀键旨在索引字符串列的特定前缀长度长度,可以更快地搜索这些前缀。了解prefix keys `这将在整个Movie_ID列上创建标准主键。主密钥对于唯一识别...
    编程 发布于2025-04-08
  • Java是否允许多种返回类型:仔细研究通用方法?
    Java是否允许多种返回类型:仔细研究通用方法?
    在Java中的多个返回类型:一种误解类型:在Java编程中揭示,在Java编程中,Peculiar方法签名可能会出现,可能会出现,使开发人员陷入困境,使开发人员陷入困境。 getResult(string s); ,其中foo是自定义类。该方法声明似乎拥有两种返回类型:列表和E。但这确实是如此吗...
    编程 发布于2025-04-08
  • 如何在其容器中为DIV创建平滑的左右CSS动画?
    如何在其容器中为DIV创建平滑的左右CSS动画?
    通用CSS动画,用于左右运动 ,我们将探索创建一个通用的CSS动画,以向左和右移动DIV,从而到达其容器的边缘。该动画可以应用于具有绝对定位的任何div,无论其未知长度如何。问题:使用左直接导致瞬时消失 更加流畅的解决方案:混合转换和左 [并实现平稳的,线性的运动,我们介绍了线性的转换。这...
    编程 发布于2025-04-08
  • 如何配置Pytesseract以使用数字输出的单位数字识别?
    如何配置Pytesseract以使用数字输出的单位数字识别?
    Pytesseract OCR具有单位数字识别和仅数字约束 在pytesseract的上下文中,在配置tesseract以识别单位数字和限制单个数字和限制输出对数字可能会提出质疑。 To address this issue, we delve into the specifics of Te...
    编程 发布于2025-04-08
  • 如何在php中使用卷发发送原始帖子请求?
    如何在php中使用卷发发送原始帖子请求?
    如何使用php 创建请求来发送原始帖子请求,开始使用curl_init()开始初始化curl session。然后,配置以下选项: curlopt_url:请求 [要发送的原始数据指定内容类型,为原始的帖子请求指定身体的内容类型很重要。在这种情况下,它是文本/平原。要执行此操作,请使用包含以下标头...
    编程 发布于2025-04-08
  • 如何简化PHP中的JSON解析以获取多维阵列?
    如何简化PHP中的JSON解析以获取多维阵列?
    php 试图在PHP中解析JSON数据的JSON可能具有挑战性,尤其是在处理多维数组时。 To simplify the process, it's recommended to parse the JSON as an array rather than an object.To do...
    编程 发布于2025-04-08
  • 如何在JavaScript对象中动态设置键?
    如何在JavaScript对象中动态设置键?
    在尝试为JavaScript对象创建动态键时,如何使用此Syntax jsObj['key' i] = 'example' 1;不工作。正确的方法采用方括号: jsobj ['key''i] ='example'1; 在JavaScript中,数组是一...
    编程 发布于2025-04-08
  • 如何同步迭代并从PHP中的两个等级阵列打印值?
    如何同步迭代并从PHP中的两个等级阵列打印值?
    同步的迭代和打印值来自相同大小的两个数组使用两个数组相等大小的selectbox时,一个包含country代码的数组,另一个包含乡村代码,另一个包含其相应名称的数组,可能会因不当提供了exply for for for the uncore for the forsion for for ytry...
    编程 发布于2025-04-08
  • 如何使用PHP从XML文件中有效地检索属性值?
    如何使用PHP从XML文件中有效地检索属性值?
    从php $xml = simplexml_load_file($file); foreach ($xml->Var[0]->attributes() as $attributeName => $attributeValue) { echo $attributeName,...
    编程 发布于2025-04-08
  • 如何将来自三个MySQL表的数据组合到新表中?
    如何将来自三个MySQL表的数据组合到新表中?
    mysql:从三个表和列的新表创建新表 答案:为了实现这一目标,您可以利用一个3-way Join。 选择p。*,d.content作为年龄 来自人为p的人 加入d.person_id = p.id上的d的详细信息 加入T.Id = d.detail_id的分类法 其中t.taxonomy =...
    编程 发布于2025-04-08
  • 如何使用Python的请求和假用户代理绕过网站块?
    如何使用Python的请求和假用户代理绕过网站块?
    如何使用Python的请求模拟浏览器行为,以及伪造的用户代理提供了一个用户 - 代理标头一个有效方法是提供有效的用户式header,以提供有效的用户 - 设置,该标题可以通过browser和Acterner Systems the equestersystermery和操作系统。通过模仿像Chro...
    编程 发布于2025-04-08
  • 为什么PYTZ最初显示出意外的时区偏移?
    为什么PYTZ最初显示出意外的时区偏移?
    与pytz 最初从pytz获得特定的偏移。例如,亚洲/hong_kong最初显示一个七个小时37分钟的偏移: 差异源利用本地化将时区分配给日期,使用了适当的时区名称和偏移量。但是,直接使用DateTime构造器分配时区不允许进行正确的调整。 example pytz.timezone(...
    编程 发布于2025-04-08

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3