六、04【Java 多线程】之并发编程

2023-11-06

多线程并发编程

并行和并发的概念我们之前有提到过。在回顾下

并发:多个任务在同一个 CPU 核上,按细分的时间片轮流(交替)执行,从逻辑上来看那些任务是同时执行。

并行:单位时间内,多个处理器或多核处理器同时处理多个任务,是真正意义上的同时进行。

那么在多线程编程实战中,线程的个数往往大于CPU的个数,所以一般都称为多线程并发编程而不是多线程并行编程。

在多核CPU时代的到来打破了单核CPU对多线程效能的限制。多个CPU意味着每个线程都可以使用自己的CPU运行,减少了线程上下文切换的开销,随着对应用系统性能和吞吐量的要求,出现了处理海量数据和请求的需求,这些对高并发多线程编程有这急需的要求。

线程安全的问题

并发编程三要素,也是线程的安全性问题所体现。

原子性:一个不可再被分割的颗粒。原子性指的是一个操作是不可中断的,即使是在多线程环境下,一个操作一旦开始就不会被其他线程影响。

可见性:一个线程对共享变量的修改,另一个线程能够立刻看到。

有序性:程序执行的顺序按照代码的先后顺序执行(处理器可能会对指令进行重排序);

出现线程安全问题的原因

1)线程切换带来的原子性问题。

2)缓存导致的可见性问题。

3)编译优化带来的有序性问题。

解决办法

  • JDK Atomic开头的原子类、synchronized、Lock解决原子性问题。
  • synchronized、volatile、Lock解决可见性问题。
  • Happens-Before 规则可以解决有序性问题。

多线程编程中,有可能会出现多个线程同时访问同一个共享&可变资源的情况,这个资源称之为临界资源。这种资源可能是:对象、变量、文件等。

共享:资源可以由多个线程同时访问。

可变:资源可以在其生命周期内被修改。

由于线程执行的过程是不可控的,所以需要采用同步机制来协同对对象可变状态的访问!

所有的并发模式在解决线程安全问题时,采用的方案都是序列化访问临界资源。也就是在同一时刻,只能有一个线程访问临界资源,也称作同步互斥访问。

Java 提供了两种方式来实现同步互斥访问:synchronized 和 Lock;

同步器的本质就是加锁;目的就是:序列化访问临界资源,即同一时刻只能有一个线程访问临界资源(同步互斥访问);

注:当多个线程执行一个方法时,该方法内部的局部变量并不是临界资源,因为这些局部变量是在每个线程的私有栈中,因此不具有共享性,不会导致线程安全问题。

那是不是说只要有多个线程去访问同一个资源就一定出现线程安全的问题?答案是否定的。

如果多个线程都是读取这个资源,那就不会出现线程安全的问题。但有一个线程去修改资源了,才会出现线程安全的问题。

synchronized

synchronized 关键字是用来控制线程同步的,就是在多线程的环境下,控制 synchronized 代码块不会被多个线程同时执行。synchronized 可以修饰类、方法、变量。

在 Java 早期版本中,synchronized属于重量级锁,效率低下,因为监视器锁(monitor)是依赖于底层的操作系统的 Mutex Lock 来实现的,Java 的线程是映射到操作系统的原生线程之上的。如果要挂起或者唤醒一个线程,都需要操作系统帮忙完成,而操作系统实现线程之间的切换时需要从用户态转换到内核态,这个状态之间的转换需要相对比较长的时间,时间成本相对较高,这也是为什么早期的 synchronized 效率低的原因。在 Java 6 之后官方从 JVM 层面对 synchronized 进行了较大优化,所以现在的 synchronized 锁效率也优化得很不错了。

JDK1.6 对锁的实现引入了大量的优化,如自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来减少锁操作的开销。

synchronized 的使用方式

修饰实例方法:作用于当前对象实例加锁,进入同步代码前要获得当前对象实例的锁。

修饰静态方法:也就是给当前类加锁,会作用于类的所有对象实例,因为静态成员不属于任何一个实例对象,是类成员( static 表明这是该类的一个静态资源,不管new了多少个对象,只有一份)。所以如果一个线程A调用一个实例对象的非静态 synchronized 方法,而线程B需要调用这个实例对象所属类的静态 synchronized 方法,是允许的,不会发生互斥现象,因为访问静态 synchronized 方法占用的锁是当前类的锁,而访问非静态 synchronized 方法占用的锁是当前实例对象锁。

修饰代码块:指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。

总结:synchronized 关键字加到 static 静态方法和 synchronized(class)代码块上都是是给 Class 类上锁。synchronized 关键字加到实例方法上是给对象实例上锁。尽量不要使用 synchronized(String a) 因为JVM中,字符串常量池具有缓存功能!

synchronized 的具体使用

1)双重校验锁实现对象单例(线程安全)

public class Singleton {
    private volatile static Singleton uniqueInstance;

    private Singleton() {}

    public static Singleton getUniqueInstance() {
        // 先判断对象是否已经实例过,没有实例化过才进入加锁代码
        if (uniqueInstance == null) {
            // 类对象加锁
            synchronized (Singleton.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}

需要注意 uniqueInstance 采用 volatile 关键字修饰也是很有必要。

uniqueInstance = new Singleton(); 这段代码其实是分为三步执行:

  1. 为 uniqueInstance 分配内存空间
  2. 初始化 uniqueInstance
  3. 将 uniqueInstance 指向分配的内存地址

但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1->3->2。指令重排在单线程环境下不会出现问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T1 执行了 1 和 3,此时 T2 调用 getUniqueInstance() 后发现 uniqueInstance 不为空,因此返回 uniqueInstance,但此时 uniqueInstance 还未被初始化。使用 volatile 可以禁止 JVM 的指令重排,保证在多线程环境下也能正常运行。

synchronized 实现原理

synchronized是Java中的一个关键字,在使用的过程中并没有看到显示的加锁和解锁过程。因此有必要通过javap命令,查看相应的字节码文件。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

六、04【Java 多线程】之并发编程 的相关文章

  • Android - 如何访问 onResume 中 onCreate 中实例化的 View 对象?

    In my onCreate 方法 我正在实例化一个ImageButton View public void onCreate Bundle savedInstanceState super onCreate savedInstanceSt
  • MP3:一种以毫秒为单位获取任何给定字节位置的位置的方法?

    我创建了一个 servlet 它返回从客户端请求的任何给定字节位置开始的流 来自 MP3 文件 这允许客户端在任何给定字节位置立即开始播放 而无需进行任何本地查找 现在 我有一个滑块可以直观地显示进度 我正在使用当前字节位置来更新滑块 但是
  • 如何打印整个字符串池?

    我想打印包含文字的整个字符串池String使用添加的对象intern 就在垃圾收集之前 JDK有没有隐式的方法来进行这样的操作 我们如何检查字符串池 EDIT The comment suggests that there may be a
  • 使用 Checkstyle Plugin 时从插件调用代码时出现问题:“org.eclipse.jface”

    我正在尝试在 Rational Software Architect 7 0 0 4 上使用 eclipse cs 插件 我最近卸载了旧的 beta2 版本并安装了 beta3 插件本身按照之前的配置工作 但是每当我尝试通过 Windows
  • Java 泛型/类型调度问题

    考虑以下程序 import java util List import java util ArrayList public class TypeTest public static class TypeTestA extends Type
  • PropertySources 中各种源的优先级

    Spring引入了新的注释 PropertySources对于所有标记为的类 Configuration since 4 0 需要不同的 PropertySource作为论证 PropertySources PropertySource c
  • 使用 Java 在浏览器中下载 CSV 文件

    我正在尝试在 Web 应用程序上添加一个按钮 单击该按钮会下载一个 CSV 文件 该文件很小 大小仅约 4KB 我已经制作了按钮并附加了一个侦听器 文件也准备好了 我现在唯一需要做的就是创建单击按钮时下载 csv 文件的实际事件 假设 fi
  • @EnableTransactionManagement 的范围是什么?

    我试图了解正确的放置位置 EnableTransactionManagement多个 JavaConfig 上下文的情况下的注释 考虑以下场景 我在 JPAConfig java 和 AppConfig java 中有 JPA 配置以及一组
  • Install4j:如何在安装结束时执行命令行 java -jar filename.jar

    在 Intall4j 中 在安装结束时 我只想通过执行如下命令行来初始化某些内容 java jar filename jar 我怎样才能归档这个任务install4j Thanks 将 运行可执行文件或批处理文件 操作添加到 安装屏幕 并设
  • 如何在java中使jpeg无损?

    有没有人可以告诉我如何使用编写 jpeg 文件losslessjava中的压缩 我使用下面的代码读取字节来编辑字节 WritableRaster raster image getRaster DataBufferByte buffer Da
  • 具有多种值类型的 Java 枚举

    基本上我所做的是为国家编写一个枚举 我希望不仅能够像国家一样访问它们 而且还能够访问它们的缩写以及它们是否是原始殖民地 public enum States MASSACHUSETTS Massachusetts MA true MICHI
  • 覆盖 MATLAB 默认静态 javaclasspath 的最佳方法

    MATLAB 配置为在搜索用户可修改的动态路径之前搜索其静态 java 类路径 不幸的是 静态路径包含相当多非常旧的公共库 因此如果您尝试使用新版本 您可能最终会加载错误的实现并出现错误 例如 静态路径包含 google collectio
  • tomcat 过滤所有 web 应用程序

    问题 我想对所有网络应用程序进行过滤 我创建了一个过滤器来监视对 apache tomcat 服务器的请求 举例来说 它称为 MyFilter 我在 netbeans 中创建了它 它创建了 2 个独立的目录 webpages contain
  • 在 AKKA 中,对主管调用 shutdown 是否会停止其监督的所有参与者?

    假设我有一位主管连接了 2 位演员 当我的应用程序关闭时 我想优雅地关闭这些参与者 调用supervisor shutdown 是否会停止所有参与者 还是我仍然需要手动停止我的参与者 gracias 阻止主管 https github co
  • OpenJDK 版本控制

    上下文 我想确保我们系统上安装的 Java 不受 CVE 2022 21449 的影响 java version 给出 openjdk version 11 0 7 2020 04 14 LTS OpenJDK Runtime Enviro
  • 如何在android sdk上使用PowerMock

    我想为我的 android 项目编写一些单元测试和仪器测试 然而 我遇到了一个困扰我一段时间的问题 我需要模拟静态方法并伪造返回值来测试项目 经过一些论坛的调查 唯一的方法是使用PowerMock来模拟静态方法 这是我的 gradle 的一
  • 从java中的字符串数组中删除空值

    java中如何从字符串数组中删除空值 String firstArray test1 test2 test4 我需要像这样没有 null 空 值的 firstArray String firstArray test1 test2 test4
  • 如何移动图像(动画)?

    我正在尝试在 x 轴上移动船 还没有键盘 我如何将运动 动画与boat png而不是任何其他图像 public class Mama extends Applet implements Runnable int width height i
  • java中如何找到class文件的包

    我正在编写一个使用 class 文件的 java 程序 我希望能够读取文件系统上的 class 文件 使用 InputStream 并确定它所在的包 该 class 文件可能不在一个好的包目录结构中 它可能位于某个随机位置 我怎样才能做到这
  • 使用 eclipse IDE 配置 angularjs

    我想开始使用 AngularJs 和 Java Spring 进行开发 我使用 Eclipse 作为 IDE 我想配置我的 Eclipse 以使这些框架无缝工作 我知道我可能要求太多 但相信我 我已经做了很多研究 你们是我最后的选择 任何帮

随机推荐

  • 课时 12 自测题

    以下哪些是 Kubernetes 中常见的监控场景 多选题 A 资源监控 B 性能监控 C 安全监控 D 事件监控 从功能性的角度而言 Heapster 的 Sink 机制比 Metrics Server 更强大 单选题 A TRUE B
  • kubernetes集群部署

    kubernetes集群部署 环境 master节点部署 安装docker 服务器配置 yum安装 新增yum源 安装kubelet kubeadm kubectl 启动master 查看启动情况 使用配置文件启动 推荐 其他节点部署 首先
  • Heap与Stack的区别

    1 heap是堆 stack是栈 2 stack的空间由操作系统自动分配和释放 heap的空间是手动申请和释放的 heap常用new关键字来分配 3 stack空间有限 heap的空间是很大的自由区 在Java中 若只是声明一个对象 则先在
  • 二进制加权DAC

    二进制加权DAC 二进制加权数模转换器是一种将数字二进制数转换成与数字数值成比例的等效模拟输出信号的数据转换器 数字到模拟转换器 或DAC 因为他们更广为人知 是相反的模拟到数字转换器 DAC将二进制或非二进制数和代码转换成模拟值 其输出电
  • caffe目标检测模型训练全过程(三)目标检测第一步

    遍历整图查找蝴蝶位置 2018 04 22 训练模型对于识别背景和蝴蝶有比较好的效果 基本对不会识别错误 接下来 将通过整图遍历的原始而又野蛮的方式对一张原始图片进行处理 进而查找到蝴蝶的具体位置 具体思路如下图 对原图进行缩放成理想大小
  • rabbitmq在商户通知中的使用代码实例

    文章目录 controller层 MQ 消息发送器 接口 RabbitMQ的配置项 RabbitMQBeanProcessor MQ 线程池配置 mq清除商户登录信息通知 mq支付订单商户通知 代码选自jeepay controller层
  • 输入npm install 报错node-sass@4.13.0 postinstall:`node scripts/build.js` Failed at the node-sass@4.13.0

    错误详情 这个是因为sass安装时获取源的问题 先修改sass安装的源 再运行npm install就成功了 npm config set sass binary site https npm taobao org mirrors node
  • Tensorflow运行出现错误: No module named 'tensorflow.contrib'

    往下查看错误描述 可以看到 ModuleNotFoundError No module named tensorflow contrib 我安装的是tensorflow 1 15 因为tensorflow1 15以后的版本都已经删除了该模块
  • 学渣带你刷Leetcode0017. 电话号码的字母组合

    题目描述 给定一个仅包含数字 2 9 的字符串 返回所有它能表示的字母组合 给出数字到字母的映射如下 与电话按键相同 注意 1 不对应任何字母 示例 输入 23 输出 ad ae af bd be bf cd ce cf 说明 尽管上面的答
  • 多线程编程技巧

    java中 多线程类需要继承Thread或实现Runnable接口 在Run函数中执行多线程代码 但是需要用Start 函数开始执行 多线程并行执行 执行的顺序取决于本地操作系统给谁分配系统资源 Runnable共享资源的方法 a 如果每个
  • [数据结构]--图(图的遍历,最小生成树,最短路径算法)

    前言 在这里 如果大家对图或者数据结构还不太熟悉 想找一个动态的生成过程来参考 这是一个不错的网站 知识框架 图的定义 在线性结构中 数据元素之间满足唯一的线性关系 每个数据元素 除第一个和最后一个外 只有一个直接前趋和一个直接后继 在树形
  • java入门到精通6版pdf_java从入门到精通第6章.pdf

    100 第 6 章 继承 第6 章 继 承 继承是面向对象编程的重要特征之一 顾名思义 继承就是在现有类的基础上构建新类 以满足新的要求 在继承过程中 新的类继承原来的方法和实例变量 并且能添加自己的方 法和实例变量 在本章中主要讲解的内容
  • 多个df文件之间含有重叠项的处理

    在进行数据分析时 为获取某个完整的panel数据往往需要将多个文件进行整合 而当这些文件之间存在重叠的时候 单纯的拼接难以得到好的结果 本文将以pandas重复值处理的若干函数着手 分析最好的文件重复值处理方法 首先 pandas中处理重复
  • CSS快速入门

    目录 一 概念 二 好处 三 CSS与html结合方式 1 内联样式 2 内部样式 3 外部样式 四 CSS语法 五 选择器 基础选择器 1 id选择器 2 类选择器 3 元素选择器 例子 扩展选择器 1 选择所有元素 2 并集选择器 3
  • qt 和java混合编程

    用QAndroidJniObject来做new一个对象 java Qt C File file new File QAndroidJniObject file java io File V File file new File tmp a
  • 大数据课程L2——网站流量项目的算法分析&&数据处理

    文章作者邮箱 yugongshiye sina cn 地址 广东惠州 本章节目的 了解网站流量项目的算法分析 了解网站流量项目的数据处理 一 项目的算法分析 1 概述 网站流量统计是改进网站服务的重要手段之一 通过获取用户在网站的行为 可以
  • [UTCTF 2020]Spectre

    题目 一段wav音频 Audacity 查看频谱图 放大即可发现flag Sonic Visualiser 上下左右拖动就找到flag了
  • 如何从数组对象中拿到指定的数据格式,数组对象数据处理

    一 原始数据 result name book4 value 3 children name 你的名字 value 3 name 言叶之庭 value 5 name book5 value 3 children name 白夜行 value
  • selenium中获取页面元素方法介绍以及定位页面元素

    1 通过浏览器驱动获取 单个元素页面元素的8种方式 通过 id获取元素 el driver find element by id id 通过 name获取元素 el driver find element by name name 通过 c
  • 六、04【Java 多线程】之并发编程

    多线程并发编程 并行和并发的概念我们之前有提到过 在回顾下 并发 多个任务在同一个 CPU 核上 按细分的时间片轮流 交替 执行 从逻辑上来看那些任务是同时执行 并行 单位时间内 多个处理器或多核处理器同时处理多个任务 是真正意义上的同时进