搜索
首页Javajava教程JAVA并发编程总结:线程安全性、对象的共享

第一章 简介

摘书

  1. 线程会共享进程范围内的资源,例如内存句柄和文件句柄,但每个线程都有各自的程序计数器(Program Counter)、栈以及局部变量等。

  2. 在同一个程序中的多个线程也可以被同时调度到多个CPU上运行。

第二章 线程安全性

摘书

  1. Java中的主要同步机制是关键字synchronized,它提供了一种独占的加锁方式,但“同步”这个术语还包括volatile类型的变量,显式锁(Explicit Lock)以及原子变量。

  2. 如果当多个线程访问同一个可变的状态变量时没有使用合适的同步,那么程序就会出现错误。有三种方式可以修复这个问题:

  • 不在线程之间共享改状态变量。

  • 将状态变量修改为不可变的变量。

  • 在访问状态变量时使用同步。

  • 线程安全性定义:当多个线程访问某个类时,这个类始终都能表现出正确的行为,那么就称这个类是线程安全的。

  • 无状态对象一定是线程安全的。

  • 大多数竞态条件的本质:基于一种可能失效的观察结果来做出判断或者是执行某个计算。这种类型的竞态条件成为“先检查后执行”:首先观察到某个条件为真(例如文件X不存在),然后根据这个观察结果采用相应的动作(创建文件X),但事实上,在你观察到这个结果以及开始创建文件之间,观察结果可能变得无效(另一个线程在这期间创建了文件X),从而导致了各种问题(未预期的异常、数据被覆盖、文件被破坏等)。

  • 假定有两个操作A和B,如果从执行A的线程来看,当另一个线程执行B时,要么将B全部执行完,要么完全不执行B,那么A和B对彼此来说是原子的。原子操作是指,对于访问同一个状态的所有操作(包括该操作本身)来说,这个操作是一个以原子方式执行的操作。

  • 在实际情况中,应尽可能地使用现有的线程安全对象(例如AtomicLong)来管理类的状态。与非线程安全的对象相比,判断线程安全对象的可能状态及其状态转换情况要更为容易,从而也更加容易维护和验证线程安全性。

  • 要保持状态的一致性,就需要在单个原子操作中更新所有相关的状态变量。

  • 重入的一种实现方法是,为每个锁关联一个获取计数值和一个所有者线程。当计数值为0时,这个锁就被认为是没有被任何线程持有。当线程请求一个未被持有的锁时,JVM将会记下锁的持有者,并且将获取计数值置为1。如果同一个线程再次获取这个锁,计数值将会递增,而当线程退出同步代码块时,计数器会相应地递减。当计数值为0时,这个锁将被释放。

  • 并非所有数据都需要锁的保护,只有被多个线程同时访问的可变数据才需要通过锁来保护。

  • 当执行时间较长的计算或者可能无法快速完成的操作时(例如,网络IO或控制台IO),一定不要持有锁。

  • 体会

    1. 状态的理解,我认为是类的成员变量。无状态对象就是成员变量不能储存数据,或者是可以储存数据但是这个数据不可变。无状态对象是线程安全的。如果方法中存在成员变量,就需要对这个成员变量进行相关的线程安全的操作。

    2. 不要一味地在方法前加synchronized,这可以保证线程安全,但是方法的并发功能会减弱,导致本来可以支持并发的方法变成堵塞,导致程序处理速度的变慢。

    3. synchronized包围的代码要尽可能的短,但是要保证有影响的所有成员变量在一起。没有关系的成员变量可以用多个synchronized包围。

    第三章 对象的共享

    摘书

    1. 加锁的含义不仅仅局限于互斥行为,还包括内存可见性。为了确保所有线程都能看到共享变量的最新值,所有执行读操作或者写操作的线程都必须在同一个锁上同步。

    2. Java语言提供了一种稍弱的同步机制,即volatile变量,用来确保将变量的更新操作通知到其他线程。当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重新排序。volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。

    3. 在访问volatile变量时不会执行加锁操作,因此也就不会使执行线程阻塞,因此volatile变量是一种比synchronized关键字更轻量级的同步机制。

    4. volatile变量通常用做某个操作完成、发生中断或者是状态的标志。volatile的语义不足以确保递增操作(count++)的原子性,除非你能确保只有一个线程对变量执行写操作。

    5. 加锁机制既可以确保可见性又可以确保原子性,而volatile变量只能确保可见性。

    6. 当且仅当满足以下所有条件时,才应该使用volatile变量:

    • 对变量的写入操作不依赖变量的当前值,或者你能确保只有单个线程更新变量的值。

    • 该变量不会与其他状态变量一起纳入不变形条件中。

    • 访问该变量时不需要加锁。

  • “发布(Publish)”一个对象的意思是指,使对象能够在当前作用域之外的代码中使用。

  • 当某个不应该发布的对象被发布时,这种情况就被称为逸出(Escape)。

  • 不要在构造过程中使this引出逸出。

  • 如果想在构造函数中注册一个事件监听器或者启动线程,那么可以使用一个私有的构造函数和一个公共的工厂方法(Factory Method),从而避免不正确的构造过程。

  • 栈封闭是线程封闭的一种特例,在栈封闭中,只有通过局部变量才能访问对象。

  • 维持线程封闭性的一种更规范方法是使用ThreadLocal,这个类能够使线程中的某个值与保存值的对象关联起来。

  • ThreadLocal对象通常用于防止对可变的单实例对象(Singleton)或全局变量进行共享。

  • 当满足以下条件时,对象才是不可变的:

    • 对象创建以后其状态就不能修改。

    • 对象的所有域都是final类型。

    • 对象是正确创建的(在对象的创建期间,this引用没有逸出)。

  • 不可变对象一定是线程安全的。

  • 要安全地发布一个对象,对象的引用以及对象的状态必须同时对其他线程可见。一个正确构造的对象可以通过以下方式来安全地发布:

    • 在静态初始化函数中初始化一个对象引用。

    • 将对象的引用保存到volatile类型的域或者AtomicReferance对象中。

    • 将对象的引用保存到某个正确构造对象的final类型域中。

    • 将对象的引用保存到一个由锁保护的域中。

  • 在没有额外的同步的情况下,任何线程都可以安全地使用被安全发布的事实不可变对象。

  • 对象的发布需求取决于它的可变性:

    • 不可变对象可以通过任意机制来发布。

    • 事实不可变对象必须通过安全方式来发布。

    • 可变对象必须通过安全方式来发布,并且必须是线程安全的或者由某个锁保护起来。

  • 在并发程序中使用和共享对象时,可以使用一些实用的策略,包括:

    • 线程封闭。线程封闭的对象只能由一个线程拥有,对象被封闭在该线程中,并且只能由这个线程修改。

    • 只读共享。在没有额外同步的情况下,共享的只读对象可以由多个线程并发访问,但任何线程都不能修改它。共享的只读对象包括不可变对象和事实不可变对象。

    • 线程安全共享。线程安全的对象在其内部实现同步,因此多个线程可以通过对象的公有接口来进行访问而不需要进一步的同步。

    • 保护对象。被保护的对象只能通过持有特定的锁来访问。保护对象包括封装在其他线程安全对象中的对象,以及已发布的并且由某个特定锁保护的对象。

    体会

    1. 发布和逸出的理解:就是说一个类中的成员变量或者对象可以被其他的类所引用使用就是发布,如用static修饰的静态变量或者是当前调用方法的对象。逸出是指该成员变量或对象在本来不应该被多线程引用的情况下暴露出去被引用,导致其值可能被错误修改的问题。一句话,不要随便扩大一个类以及内部使用成员变量和方法的作用域。这也是封装应该考虑的问题。

    2. this逸出:即在构造方法的内部类中启动另一个线程引用了这个对象,但是这时这个对象还没有构造完成,可能会导致出乎意料的错误。解决方法是创建一个工厂方法,然后将构造器设置成私有构造器。

    3. final修改的成员变量需要在构造器在构造器中初始化,否则对象实例化后这个成员变量不能赋值。final修饰的成员变量是引用对象时,这个对象的地址不能修改,但是这个对象的值是可以修改的。

    4. 安全发布一个对象的四种方式的理解,如A类中有B类的引用:

    • A的静态初始化方法,如public static A a = new A(b);这样的静态工厂类中,引用B的时候初始化B。

    • A类中的B成员变量用volatile b或者是AtomicReferance b这样修饰。

    • A类中的B成员变量用final B b这样修饰。

    • A类中的方法使用到B的时候用synchronized(lock){B…}包围。

  • 事实不可变对象很简单的理解就是技术上是可变的,但是在业务逻辑处理中是不会去修改的对象。

  • 相关推荐:

    Java 线程全和共享资源

    java 线程安全和不可变性

    以上是JAVA并发编程总结:线程安全性、对象的共享的详细内容。更多信息请关注PHP中文网其他相关文章!

    声明
    本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
    2025年的前4个JavaScript框架:React,Angular,Vue,Svelte2025年的前4个JavaScript框架:React,Angular,Vue,SvelteMar 07, 2025 pm 06:09 PM

    本文分析了2025年的前四个JavaScript框架(React,Angular,Vue,Susve),比较了它们的性能,可伸缩性和未来前景。 尽管由于强大的社区和生态系统,所有这些都保持占主导地位,但它们的相对人口

    Spring Boot Snakeyaml 2.0 CVE-2022-1471问题已修复Spring Boot Snakeyaml 2.0 CVE-2022-1471问题已修复Mar 07, 2025 pm 05:52 PM

    本文介绍了SnakeyAml中的CVE-2022-1471漏洞,这是一个允许远程代码执行的关键缺陷。 它详细介绍了如何升级春季启动应用程序到Snakeyaml 1.33或更高版本的降低风险,强调了依赖性更新

    如何使用咖啡因或Guava Cache等库在Java应用程序中实现多层缓存?如何使用咖啡因或Guava Cache等库在Java应用程序中实现多层缓存?Mar 17, 2025 pm 05:44 PM

    本文讨论了使用咖啡因和Guava缓存在Java中实施多层缓存以提高应用程序性能。它涵盖设置,集成和绩效优势,以及配置和驱逐政策管理最佳PRA

    Java的类负载机制如何起作用,包括不同的类载荷及其委托模型?Java的类负载机制如何起作用,包括不同的类载荷及其委托模型?Mar 17, 2025 pm 05:35 PM

    Java的类上载涉及使用带有引导,扩展程序和应用程序类负载器的分层系统加载,链接和初始化类。父代授权模型确保首先加载核心类别,从而影响自定义类LOA

    冰山:数据湖桌的未来冰山:数据湖桌的未来Mar 07, 2025 pm 06:31 PM

    冰山是用于大型分析数据集的开放式桌子格式,可提高数据湖的性能和可伸缩性。 它通过内部元数据管理解决了镶木quet/orc的局限

    Node.js 20:关键性能提升和新功能Node.js 20:关键性能提升和新功能Mar 07, 2025 pm 06:12 PM

    Node.js 20通过V8发动机改进可显着提高性能,特别是更快的垃圾收集和I/O。 新功能包括更好的WebSembly支持和精制的调试工具,提高开发人员的生产率和应用速度。

    如何共享黄瓜中的步骤之间的数据如何共享黄瓜中的步骤之间的数据Mar 07, 2025 pm 05:55 PM

    本文探讨了在黄瓜步骤之间共享数据的方法,比较方案上下文,全局变量,参数传递和数据结构。 它强调可维护性的最佳实践,包括简洁的上下文使用,描述性

    如何在Java中实施功能编程技术?如何在Java中实施功能编程技术?Mar 11, 2025 pm 05:51 PM

    本文使用lambda表达式,流API,方法参考和可选探索将功能编程集成到Java中。 它突出显示了通过简洁性和不变性改善代码可读性和可维护性等好处

    See all articles

    热AI工具

    Undresser.AI Undress

    Undresser.AI Undress

    人工智能驱动的应用程序,用于创建逼真的裸体照片

    AI Clothes Remover

    AI Clothes Remover

    用于从照片中去除衣服的在线人工智能工具。

    Undress AI Tool

    Undress AI Tool

    免费脱衣服图片

    Clothoff.io

    Clothoff.io

    AI脱衣机

    AI Hentai Generator

    AI Hentai Generator

    免费生成ai无尽的。

    热门文章

    仓库:如何复兴队友
    1 个月前By尊渡假赌尊渡假赌尊渡假赌
    R.E.P.O.能量晶体解释及其做什么(黄色晶体)
    2 周前By尊渡假赌尊渡假赌尊渡假赌
    Hello Kitty Island冒险:如何获得巨型种子
    1 个月前By尊渡假赌尊渡假赌尊渡假赌

    热工具

    SublimeText3 Mac版

    SublimeText3 Mac版

    神级代码编辑软件(SublimeText3)

    适用于 Eclipse 的 SAP NetWeaver 服务器适配器

    适用于 Eclipse 的 SAP NetWeaver 服务器适配器

    将Eclipse与SAP NetWeaver应用服务器集成。

    ZendStudio 13.5.1 Mac

    ZendStudio 13.5.1 Mac

    功能强大的PHP集成开发环境

    mPDF

    mPDF

    mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),

    SublimeText3 英文版

    SublimeText3 英文版

    推荐:为Win版本,支持代码提示!