《Java并发编程实战》学习笔记(2)

自定义css属性实现渐变边框动画 自定义cs属性实现渐变边框动画 阅读详情

第三章:共享对象

可见性

除了synchronize,同步还具有另一个重要、微妙的方面:内存可见性。我们不仅希望能够避免一个线程修改其他线程正在使用的对象的状态,而且希望确保当一个线程修改了对象的状态后,其他的线程能够真正看到改变。

在一个单线程化的环境里,如果向一个变量先写入值,然后在没有写干涉的情况下读取这个变量,是可以得到相同的返回值。但是当读和写发生在不同的线程中时,情况却根本不是这样。

/*
	读线程启动后,主线程虽然把ready赋值为true,
	但是由于线程之间存在缓存问题,读线程的reday仍然是false,未能及时获取最新的reday值。
	导致程序的运行结果与预期不符。
*/
public class NoVisibility {
  private static boolean ready;
  private static int number;

  private static class ReaderThread extends Thread {
    @Override
    public void run() {
      while (!ready) {
        Thread.yield();
      }
      System.out.println(number);
    }
  }

  public static void main(String[] args) {
    new ReaderThread().start();
    number = 42;
    ready = true;
  }
}

锁和可见性

当线程A执行一个同步块时,线程B也随后进入了被同一个锁监视的同步块中,这时可以保证,在锁释放之前对A可见的变量的值,B获得锁之后同样是可见的。
在这里插入图片描述

锁不仅仅是关于同步与互斥的,也是关于内存可见的。为了保证所有线程都能够看到共享的、可变变量的最新值,涉及同一共享变量的读取和写入线程必须使用公共的锁进行同步。


volatile变量

Java语言也提供了其他的选择,即一种同步的弱形式:volatile变量。

它确保对一个变量的更新以可预见的方式告知其他的线程。

当一个域声明为volatile类型后,编译器与运行时会监视这个变量:它是共享的,而且对它的操作不会与其他的内存操作起被重排序。

所以,读一个volatile类型的变量时,总会返回由某一线程所写入的最新值。

访问volatile变量的操作不会加锁,也就不会引起执行线程的阻塞,这使得volatile变量相对于sychronized而言,只是轻量级的同步机制。

加锁可以保证可见性与原子性;volatile变量只能保证可见性。

只有满足了下面所有的标准后,你才能使用volatile变量:

  • 写入变量时并不依赖变量的当前值,或者能够确保只有单一的线程修改变量的值;
  • 变量不需要 与其他的状态变量共同参与不变约束;
  • 而且访问变量时,没有其他的原因需要加锁。

备注:原子变量(java.util.concurrent.atomic)


线程封闭

访问共享的、可变的数据要求使用同步。一个可以避免同步的方式就是不共数据。如果数据仅在单线程中被访问,就不需要任何同步。

线程封闭(Thread confinement)技术是实现线程安全的最简单的方式之一。

当对象封闭在一个线程中时,这种做法会自动成为线程安全的,即使被封闭的对象本身并不是。

一种常见的使用线程限制的应用程序是应用池化的JDBC ( Java Database Connectivity) Connection对象。

JDBC规范并没有要求Connection对象是线程安全的。

每个线程都会同步地处理大多数请求(比如 Servlet 请求或者EJB (Enterprise Java Bean)调用),而且在Connection对象在被归还前,池不会将它再分配给其他线程,因此,这种连接管理模式隐式地将Connection对象限制在处于请求处理期间的线程中。

栈限制

栈限制(也称线程内部或者线程本地用法,但是不要与核心库类的ThreadLocal混淆)是线程限制的一种特例, 在栈限制中,只能通过本地变量才可以触及对象。

正如封装使不变约束更容易被保持,本地变量使对象更容易被限制在线程本地中。

本地变量本身就被限制在执行线程中,它们存在于执行线程栈。其他线程无法访问这个栈。

ThreadLocal

一种维护线程限制的更加规范的方式是使用Threadlocal,它允许你将每个线程与持有数值的对象关联在一起。ThreadLocal提供了get与set访问器,为每个使用它的线程维护一份单独的拷贝。所以get总是返回由当前执行线程通过set设置的最新值。


不可变性

创建后状态不能被修改的对象叫做不可变对象。

不可变对象永远是线程安全的。

无论是Java语言规范还是Java存储模型都没有关于不可变性的正式定义,但是不可变性并不简单地等于将对象中的所有域都声明为final类型,所有域都是final类型的对象仍然可以是可变的,因为final域可以获得一个到可变对象的引用。

只有满足如下状态,一个对象才是不可变的:

  • 它的状态不能在创建后再被修改;
  • 所有域都是final类型;并且,
  • 它被正确创建(创建期间没有发生this引用的逸出)。

String是一个Java中不可变对象的典型例子。


第四章:组合对象

设计线程安全的类

设计线程安全类的过程应该包括下面3个基本要素:

  • 确定对象状态是由哪些变量构成的;
  • 确定限制状态变量的不变约束;
  • 制定一个管理并发访问对象状态的策略。

名词解释:

  • 先验条件(precondition):针对方法(method),它规定了在调用该方法之前必须为真的条件。例如,你无法从空队列中移除一一个条目:在你删除元素前,队列必须处于“非空”状态。
  • 后验条件(postcondition):也是针对方法,它规定了方法顺利执行完毕之后必须为真的条件。例如,如果+1自增计数器当前状态是17,下一个唯一合法的状态是18。

实例限制

即使一个对象不是线程安全的,仍然有许多技术可以让它安全地用于多线程程序。

比如,你可以确保它只被单一的线程访问(线程限制),也可以确保所有的访问都正确地被锁保护。

将数据封装在对象内部,把对数据的访问限制在对象的方法上,更易确保线程在访问数据时总能获得正确的锁。

/**
 * 使用限制确保线程安全
 */
@ThreadSafe
public class PersonSet {
    @GuardedBy("this")
    private final Set<Person> mySet = new HashSet<Person>();

    public synchronized void addPerson(Person p) {
        mySet.add(p);
    }
    public synchronized boolean containsPerson(Person p) {
        return mySet.contains(p);
    }
}

平台类库中有很多线程限制的实例,包括一些类,它的存在就是为了把非线程安全的类转化为线程安全的。

ArrayList和HashMap这样的基本容器类是非线程安全的,但是类库提供了包装器工厂方法(Collections.synchronizedList及其同族的方法),使这些非线程安全的类可以安全地用于多线程环境中。


向已有的线程安全类添加功能

“缺少即加入(put-if-absent)”的概念相当直观一一在向容器中添加一个元素前先查看它是否已经存在,如果存在,就不添加。

(“检查再运行(check-then-act) ”的警钟现在该响起了)。

对类的线程安全需求会隐式地加入另一个条件一一像 “缺少即加入”这样的操作必须是原子的。

任何有合理的要求都会告诉你,如果你有一个不包含对象X的List,并且执行两次“缺少即加入”的addX,那么结果容器只应该包含一个X的拷贝。

但是,倘若“缺少即加入”操作不是原子的,在一些偶发的时序中,两个线程都看到X不在容器中,并且都执行了addX,导致容器中包含两份X的拷贝。

  • 添加一个新原子操作的最安全的方式是,修改原始的类,以支持期望的操作。
    但是你可能无法访问源代码或者没有修改的自由,所以这通常是不可能的。
    即使你可以修改原始的类,也需要理解其实现的同步策略,才能在维持原有设计的前提下完善它的功能。
    直接向类中加入新方法,意味着所有实现类同步策略的代码仍然包含在一个源代码文件中,因此便于理解与维护。

  • 另一种方法是扩展这个类,假如这个类在设计上是可以扩展的。
    清单4.13的BetterVector扩展了Vector,并添加了一个putIfAbsent方法。虽然扩展Vector的做法相当直观,但是并非所有的类都给子类暴露了足够多的状态,以支持这种方案。

@ThreadSafe
public class BetterVector<E> extends Vector<E> {
  public synchronized boolean putIfAbsent(E x) {
    boolean absent = !contains(x);
    if (absent) {
      add(x);
    }
    return absent;
  }
}
基于YOLOv8的铁路轨道异物检测系统设计与实现 目标检测是计算机视觉领域的核心任务之一,旨在识别图像中特定目标的位置和类别。传统方法主要依赖手工特征(如HOG、SIFT等)和分类器(如SVM),而深度学习方法通过卷积神经网络自动学习特征表示,显著提升了检测性能。YOLO(You Only Look Once)系列算法作为单阶段目标检测的代表,以其高效的检测速度著称。YOLOv8是Ultralytics公司于2023年发布的最新版本,在精度和速度上都有进一步提升。 阅读详情

相关推荐

手把手教你设计2bit反射型超表面:从单patch扫参到pin管配置全流程

本文提供了一份2bit反射型超表面设计的完整工程实践指南。从单贴片谐振器的参数化扫描与“270度法则”应用,到PIN二极管等效电路模型的获取与集成,再到利用参数化扫描高效仿真四种工作状态,手把手指导读者完成从单元设计到阵列布局、偏置线集成及加工考量的全流程,旨在帮助射频工程师将概念转化为可加工测试的实体模型。

java5的博客 1020

java线程笔记

1 线程安全(thread safety) 构建并发程序也要正确使用线程和锁。编写线程安全的代码,本质上就是管理对状态的访问,而且通常都是共享的、可变的状态。 通俗的说,一个对象的状态就是它的数据,存储在状态变量中,比如实例域或静态域。对象的状态还包括了其他附属对象的域,如HashMap的状态一部分存储到对象本身中,但同时也存储到很多Map.Entry对象中。 共享:指...

nx7758520的博客 184

2021年软件测试面试题大全

简述测试流程: 1、阅读相关技术文档(如产品PRD、UI设计、产品流程图等)。 2、参加需求评审会议。 3、根据最终确定的需求文档编写测试计划。 4、编写测试用例(等价类划分法、边界值分析法等)。 5、用例评审(主要参与人员:开发、测试、产品、测试leader)。 6、开发提交代码至SVN或者GIT ,配管搭建测试环境。 7、执行测试用例,记录发现的问题。 8、验证bug与回归测试。 9、编写测试报告。 10、产品上线。 补充测试用例设计过程: 根据需求得出测试需求 设计测试方

hard_days的博客 20万+

《Java并发编程实战》学习笔记之 第2章 线程安全性

1.什么是线程安全性当多个线程访问某个类时,不管运行时环境采用何种调度方式或者现线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么这个类就是线程安全的。在线程安全的类中封装了必要的同步机制,因此调用者无需进一步采取同步措施。无状态对象一定是线程安全的。 2.竞态条件在并发编程时,由于不恰当的执行时序而出现不正确的结果。比如,先检查后执行。3.加锁机制要...

austboy123的博客 243

zemax---光学设计基础概念篇(持续更新中)

光学设计相关知识概念

u011699626的博客 1145

7.29华为OD机试真题 新系统 - 能量对撞 (Java/Py/C/C++/Js/Go)

7.29华为OD机试真题 新系统 - 能量对撞 (Java/Py/C/C++/Js/Go)

算法大师! 1315

《Java 并发编程实战》学习笔记一

多核时代每颗 CPU 都有自己的缓存,这时 CPU 缓存(三级缓存)与内存的数据一致性就容易出现问题了,当多个线程在不同的 CPU 上执行时,这些线程操作的是不同的 CPU 缓存。比如,线程 A 操作的是 CPU-1 上的缓存,而线程 B 操作的是 CPU-2 上的缓存,很明显,这个时候线程 A 对变量 V 的操作对于线程 B 而言就不具备可见性了。:高级语言里一条语句往往需要多条 CPU 指令完成,例如count += 1这句代码,至少需要三条 CPU 指令。

yibushao的博客 1152

一文掌握 MongoDB 详细安装与配置(Windows / Linux / macOS 全平台)

本文详细介绍了MongoDB社区版7.0+在Windows、Linux和macOS三大操作系统上的完整安装、配置与安全加固流程。主要内容包括:各平台推荐安装方式(Windows使用MSI安装包、Linux通过官方仓库、macOS通过Homebrew)、基本配置参数说明、服务启动方法以及关键安全措施(如创建管理员用户和启用身份验证)。文章还提供了系统最低要求、下载地址、数据目录设置建议以及生产环境使用注意事项,适合开发、测试及生产环境部署参考。

数据知道的博客 6709

【极客时间】《Java并发编程实战》学习笔记

【极客时间】《Java并发编程实战》学习笔记

Mr.Cui的Java学习之路 2648

全新YOLOv11一文弄懂 | YOLOv11网络结构解读 、yolov11.yaml配置文件详细解读与说明、模型训练参数详细解析 | 通俗易懂!入门必看系列!

本文全面介绍了YOLOv11目标检测算法,包含网络结构解析、yaml配置文件详解及训练教程。YOLOv11通过改进的C3k2f模块、SPPF和C2PSA注意力机制等创新设计,在COCO数据集上实现了比YOLOv8更优的性能。文章详细解读了yolov11.yaml配置文件的参数设置、主干网络(backbone)和头部网络(head)结构,并提供了完整的训练代码示例和参数说明。训练部分涵盖了模型选择、参数设置及优化技巧,包括epochs、batch size、学习率等关键参数的调整建议。最后总结了YOLOv11

Ai缝合怪、助力小伙伴高效发论文 3万+

当AI写小说——一个被严重低估的工程化战场

摘要 AI小说创作远非简单的prompt生成,而是一项需要系统化工程管理的复杂任务。本文通过真实案例分析,揭示了当前AI写小说存在的五大核心难题:设定一致性难以维持、文风控制缺乏个性、爽感节奏难以把握、长篇记忆能力有限以及平台规则适配困难。研究显示,90%的失败案例源于错误的创作方法,主要表现为对话框式随机生成和模板式同质化创作。要实现高质量的AI小说创作,必须建立包括设定治理、记忆管理、文风校准等在内的全链路工程化体系,而非依赖单一prompt指令。只有采用系统化的人机协同方法,才能突破当前AI创作的质量

sellyogurt的博客 680

Java并发编程实战(2)

该系列文章是博主学习笔记,禁止转载,读书交流群:946541246 文章目录Java并发编程实战三四章学习加锁与可见性volatile Java并发编程实战三四章学习 加锁与可见性 锁可以用于确保某个线程以一种可预测的方式来查看另一个线程的执行结果 当线程A执行某个同步代码时,线程B随后进入由同一个锁保护的同步代码块,在这种情况下可以保证,在锁被释放钱,A看到的变量值在B获得锁后同样可以由B看到。...

谢耀眼 1008

Sigrity System Explorer Transient Analysis模式进行瞬态仿真分析操作指导-单端信号-串联端接

Sigrity System Explorer Transient Analysis模式可以用于瞬态仿真分析,通过搭建简易拓扑用于前仿真分析通道的时域性能,下面搭建一个简易的单端拓扑并且进行串联端接,以下图为例,准备好发送接收端的IBIS模型, (se_example.ibs,本例发送接收端都使用相同的IBIS模型),以及传输线的S参数模型(W1_welement_se.sp)最上方的图形界面是传输线的模型,本例是一个上下都有参考平面的带状线,右侧的梯形是蚀刻角度,根据W1和W2以及铜厚来确定。

weixin_54787054的博客 338

Java并发编程实战总结

Java并发编程

qq_39817635的博客 1547

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

《JAVA并发编程的艺术》之 Java并发编程实战

《JAVA并发编程的艺术》之 Java并发编程实战 文章目录《JAVA并发编程的艺术》之 Java并发编程实战生产者和消费者模式生产者消费者模式实战多生产者和多消费者场景线程池与生产消费者模式线上问题定位性能测试查看网络流量。查看系统平均负载。查看系统内存情况。查看CPU的利用率。异步任务池 当你在进行并发编程时,看着程序的执行速度在自己的优化下运行得越来越快,你会觉得越来越有成就感,这就是并发编程的魅力。但与此同时,并发编程产生的问题和风险可能也 会随之而来。本章先介绍几个并发编程的实战案例,然后再介绍如

weixin_44019182的博客 1319

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

【Java并发编程实战】

本篇是阅读《Java 并发编程实战》的零碎笔记,整体非常凌乱,并不适合阅读,仅做个人备忘。

猫吻鱼的博客 1473

Java并发编程:深入探索与实战案例

Java并发编程是一个复杂而强大的领域,它提供了多种工具和技术来应对多线程环境下的挑战。从基础的到高级的 ` LockAtomic `类,再到线程池和并发集合,Java为开发者提供了丰富的选择。通过理解这些工具的工作原理,并结合实际场景进行应用,我们可以构建出高性能、可扩展的并发应用程序。本文介绍的实战案例,展示了如何将这些知识应用于构建一个简单但高效的Web服务器,希望对读者有所启发。

运维人生 1461

Java 并发编程的艺术

内容简介 书中采用循序渐进的讲解方式,从并发编程的底层实现机制入手,逐步介绍了在设计 Java 并发程序时各种重要的技术、设计模式与应用,同时辅以丰富的示例代码,使得开发人员能够更快地领悟 Java 并发编程的要领,围绕着 Java 平台的基础并发功能快速地构建大规模的并发应用程序。 第1章介绍 Java 并发编程的挑战,会向读者说明可能会遇到哪些问题,以及如何解决。 第2章 Java 并发编程...

GitChat 2691

【免费下载】 Java 并发编程实战高清PDF版

Java 并发编程实战高清PDF版 【下载地址】Java并发编程实战高清PDF版 本仓库提供了一份名为《Java 并发编程实战高清PDF版》的资源文件,该文件深入讲解了Java并发编程技术,涵盖了多线程、锁机制以及Java内存模型等关键内容 ...

gitblog_09761的博客 2295

Java并发编程实战(1)

该系列文章是博主学习笔记,禁止转载,读书交流群:946541246 · 文章目录Java并发编程实战一二章学习概念无状态竟态条件不变性条件相互约束的变量要在一个原子操作中进行复合操作 Java并发编程实战一二章学习 概念 无状态 既不包含任何域,也不包含任何对其他类中域的引用。 无状态一定是线程安全的 例如 @ThreadSafe public class StatelessFactorizer...

谢耀眼 467

java并发编程实战源码_Java 并发编程实战-随书源码

压缩包 : Java 并发编程实战-随书源码.rar 列表jcip-examples-src/META-INF/MANIFEST.MFjcip-examples-src/net/jcip/examples/Animals.javajcip-examples-src/net/jcip/examples/AtomicPseudoRandom.javajcip-examples-src/net/jcip...

weixin_31887307的博客 347

最强Java并发编程实战学习笔记!

随着经济环境下行,大厂降本增效、筛除了一部分冗余岗位,原本荒蛮的 IT 行业发展正在逐步进入正轨中。虽说依旧算是不温不火,但在今年的技术招聘市场上,Java依旧是当仁不让的霸主!Java 有着极其成熟的生态,这个不用我多说;Java 在运维、可观测性、可监控性方面都有着非常优秀的表现;Java 也在积极应对容器化、云原生等大趋势,比如 Spring Boot 就已经全面拥抱云原生。企业对Jav...

Java知音 327

【目标设定】Java 并发编程

1. 目标是什么 掌握Java并发编程的使用 掌握Java并发编程的原理 阅读《Java并发编程的艺术》 阅读《Java并发编程实战》 输出 学习笔记5 篇及以上 2. 完成目标有什么好处 习得Java并发编程技能 补充技能树的技能点 3. 计划多长时间完成目标 2019/10/15 - 2019/11/15 4. 完成目标的途中有哪些障碍 没有时间学习 被其他的学习目标吸引 5....

Penn的博客 138

为什么我强烈推荐Java程序员学习《Java并发编程实战》专栏?

王宝令老师的《Java并发编程实战》专栏不仅仅是在讲技术,更是在传授一种学习方法和思维模式- “跳出来,看全景”和“钻进去,看本质”。这种思维方式对我学习其他技术也有很大帮助。如果你也在并发编程的学习路上感到迷茫和困惑,我强烈推荐你学习这个专栏。投资99元学习这个专栏,可能会成为你技术生涯中回报率最高的一笔投资。技术成长没有捷径,但选对学习路径可以少走弯路。希望我的推荐能帮助你做出正确的选择!点击这里立即订阅《Java并发编程实战》专栏,开启你的并发编程进阶之路!

2025博客之星Top81。专注AI工程化与架构实战。从分布式思维到模型部署,用工程化视角为你厘清AI落地的真实路径。 957

Java并发编程实战基础概要

Java线程之间的通信由Java内存模型(JMM)控制。JMM定义了线程和主内存之间的抽象关系在内存里,Java内存模型规定了所有的变量都存储在主内存(物理内存)中,每条线程还有自己的工作内存,线程对变量的所有操作都必须在工作内存中进行。不同的线程无法访问其他线程的工作内存里的内容。我们可以使用下图来表示在逻辑上线程、主内存、工作内存的三者交互关系。额外的延伸知识,从JVM的角度来看java运行时的内存区域,来对应我们Java内存模型前面我们都理解了缓存导致了可见性问题,编译优化导致了有序性问题。.....

C. 785

Java并发编程实战.pdf资源介绍:Java并发编程领域的权威指南

Java并发编程实战.pdf资源介绍:Java并发编程领域的权威指南 【下载地址】Java并发编程实战.pdf资源介绍 《Java并发编程实战》是Java并发编程领域的经典著作,全面涵盖了线程管理、并发集合、同步器、锁、线程安全等核心内容。本书结构清晰,实例丰富,适合Java初学者和经验丰富的开发者。通过深入学习,你将掌...

gitblog_06715的博客 1187

java并发编程实战

java并发编程实战 文章目录java并发编程实战死锁避免发生死锁破坏占用且等待条件破坏不可抢占条件破坏循环等待条件等待-通知机制用 synchronized 实现等待 - 通知机制调用方法时须注意尽量使用 notifyAll()07 | 安全性、活跃性以及性能问题安全性问题数据竞争、竞态条件活跃性问题性能问题08 | 管程:并发编程的万能钥匙什么是管程并发编程领域两大核心问题wait() 的正确姿势notify() 何时可以使用总结09 | Java线程(上):Java线程的生命周期通用的线程生命周期Ja

yuanwenkai 903
上一篇: 《Java并发编程实战》学习笔记(1)
下一篇: 【问题总结】由于Spring事务使用不当引起的数据库死锁
章 鱼
博客等级 码龄10年 203粉丝 105原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值