Java---多线程

news2024/11/29 7:54:53

并发编程

多线程

在同一段时间内一台计算机的内部有多个线程正在运行,一台计算机一次可以处理多个任务。

多线程的优点
  • 提高CPU的利用率:计算机中一个任务的执行不是一直都会使用CPU,也有可能在任务执行的过程中出现缺少资源的情况,这时CPU不会继续执行任务,而是会等待获取到资源之后才会继续往下执行,CPU在等待资源的过程中如果没有其他的任务需要执行,这时CPU就会处于一种忙等的状态,而如果此时计算机内还存在其他需要执行的任务,这时CPU就不会在那里干等着,而是会执行其他需要执行的任务,从而提高CPU的利用率。
多线程的缺点
  • 线程安全问题:当多个线程访问计算机中的同一资源时可能会出现数据不一致的问题
  • 线程调度问题:当多个线程都需要访问CPU的权限时,可能会出现某些线程长时间不被访问出现的线程饥饿问题
  • 上下文切换开销较大:计算机在运行多个线程时为了公平性以及不被人所察觉程序运行的卡顿,因此计算机通常会采用时间片轮转的方式不停地切换CPU的执行权限,并且由于轮转的速度很快从而增加了上下文切换的开销

并行与并发

并行

同一时刻多个任务同时执行

并发

同一时间段内多个任务轮流执行

​ 如今的计算机中并行和并发是同时执行的,如果计算机中只有一个CPU并且存在多个线程,那么计算机中只会存在并发,但是当计算机中存在多个CPU时,由于一个CPU在同一时刻只能执行一个任务,但是多个CPU在同一时刻就可以执行多个任务,于是并行和并发也就同时存在。

Java内存模型

​ 计算机内存模型是为了解决计算机在多线程环境下可见性、原子性、有序性问题,从而提出的针对程序访问及修改数据的一种规范。而Java内存模型也是一种规范,它提出的规范也同样解决了多线程环境下的问题。

JVM的主内存与工作内存

​ Java内存模型中规定所有的变量都存放在主内存当中,当存在线程对变量进行操作时,会将变量进行复制,变为变量副本存储在工作内存当中,从而对工作内存中的变量副本进行操作,每个线程的工作内存是独立的。

并发线程核心问题

可见性

​一个线程对共享变量进行修改后,其他线程可以立即得知这一操作或者这一操作的结果。其产生的原因是因为计算机中cpu的速度越来越快,而主存(内存)的速度跟不上cpu的速度,于是出现了缓存(高速缓冲存储器),位于cpu和主存之间,cpu与缓存进行交互将修改数据的结果存入缓存当中,再经过一段时间统一存入主存当中。一个cpu拥有的缓存是独立的,不能访问其他cpu的缓存,如果多个cpu访问同一变量,就可能会因为缓存的更新不及时而导致可见性问题。

public class BTest {

    public static boolean flag = false;

    public static void main(String[] args) throws IOException {
        Thread thread1 = new Thread(() -> {
            System.out.println("thread1 start...");
            try {
                Thread.sleep(120);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            flag = true;
            System.out.println("thread1 end...");
        });
        Thread thread2 = new Thread(() -> {
            System.out.println("thread2 start...");
            while (!flag) {

            }
            System.out.println("thread2 end...");
        });
        thread1.start();
        thread2.start();
    }
}

​上面的代码运行的结果就是thread2线程永远都不会停下来,其原因就是thread2线程没有感知到thread1线程中的flag变量已经被修改。

要解决上面出现的问题,只需要给flag变量添加volatile修饰即可。

在单线程环境下不存在可见性问题,因为单线程环境下计算机只有一个cpu和缓存,其中的数据都是可见的。

有序性

​在多线程环境下代码的执行顺序可能被编译器或者处理器进行指令重排,导致代码执行顺序与预期有所不同,而导致程序运行的结果与预期有所不同,从而导致有序性问题的出现。

public class Main {
    private static boolean flag = false;// volatile
    private static int x = 0;// volatile

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            x = 1;// 第一行
            flag = true;// 第二行
        });

        Thread thread2 = new Thread(() -> {
            while (!flag) {
                // 等待flag变为true
            }
            System.out.println("x = " + x);
        });

        thread1.start();
        thread2.start();
    }
}

​正常情况下不论是thread1先执行还是thread2先执行,程序都会输出1,假设第一行和第二行发生了指令重排,那么在多线程环境下,当执行到flag = true时时间片结束,此时由于x还没有来得及赋值,所以程序可能会输出0,此时就出现了有序性问题。

public class Singleton {
  static Singleton instance;// volatile
  static Singleton getInstance(){
    if (instance == null) {
      synchronized(Singleton.class) {
        if (instance == null)
          instance = new Singleton();
      }
    }
    return instance;
  }
}

​正常情况下在synchronized代码块外层再添加一层判断可以在instance不为null的情况下提高代码的执行速度,但是如果在创建对象的时候发生了指令重排,instance对象还没有来得及初始化,但是其地址已经进行了赋值,此时instance不为null,这样其他线程在调用getInstance()方法时返回的对象可能是一个还未初始化完成的对象,从而出现问题。

指令重排发生的条件

​编译器和处理器为了程序运行的速度更块,需要满足在单线程的条件下对指令的重排不会出现问题,但是在多线程的条件下就有可能出现问题,就比如上面提到的两个例子,当然指令重排也会存在cpu层面的重排。

原子性

​在多线程环境下对共享资源的操作需要满足原子性,既要么全部执行,要么全部不执行,如果对共享资源的操作只执行一部分,就有可能导致最后计算的结果出现问题。

count = 0;
count++;

count = 0;// 第一步 获取count的值
count = count + 1;// 第二步 对count进行操作
count = 1;// 第三步 将结果进行覆盖

​ 假设存在两个线程都需要进行count++操作,thread1执行到第一步之后时间片结束,thread2执行完此时count的值为1,然后thread1再执行最后的结果仍然为1,而正确的结果应该为2。

​ 需要设置volatile并且加锁解决问题,如果只是单纯的加锁,那么如果两个线程不是在同一个cpu中运行,由于cpu缓存是独立的,就有可能导致多线程下的可见性问题。

并发线程核心问题的解决

可见性

​Java中使用volatile关键字可以确保在多线程环境下共享数据的可见性,其原理是在对数据进行读操作时会先去主存当中读取数据,将数据读取到缓存当中,再从缓存中读取数据,写操作也是一样,会直接将数据的结果写入到缓存当中区,在从缓存将数据写入到主存当中,这种操作相当于牺牲了一部分缓存来换取共享数据的可见性。

在这里插入图片描述

有序性

​Java中的volatile关键字可以禁止编译器的指令重排,但是不能完全禁止处理器的指令重排,编译器不会将使用volatile关键字修饰的变量的前后进行指令重排。

原子性

​Java中可以使用同步机制(锁)+volatile解决大部分的多线程情况下的原子性问题

volatile关键字

指令重排在Java层面(Java字节码)没有重排

Hotspot(Java虚拟机)中gcc编译阶段进行了重排(JIT即时编译:代码优化的一种解决方案)

  • 优化无效代码

  • 进行简单运算

  • 指令重排

cpu在进行乱序执行时需要满足一个语义:as-if-serial语义,意思是cpu在进行乱序执行后的结果要与单线程下运行的结果相同。

在超高并发情况下才会出现指令重排,有关cpu切换的问题

屏障:不想让代码执行本该执行的样子

编译屏障

​编译屏障是编译器以及处理器主动使用的,当一段重排后的代码不满足as-if-serial语义时,编译器会主动添加内存屏障防止代码进行重排。

​编译时防止某段代码被优化掉而添加的屏障,存在一段代码可能是无效的,但是却充当着内存屏障的作用,我们不希望在执行时被优化掉,于是添加编译屏障。

内存屏障

​内存屏障是和volatile关键字相关的,当使用volatile关键字修饰变量时,Java中的编译器和虚拟机会添加内存屏障,用于实现指令重排的禁止以及强制读写数据。

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

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

相关文章

骨传导耳机危害有哪些?骨传导耳机值得入手吗?

事实上,只要是正常使用,骨传导耳机并不会对身体造成伤害,并且在众多耳机种类中,骨传导耳机可以说是相对健康的一种耳机,这种耳机最独特的地方便是声波不经过外耳道和鼓膜, 而是直接将人体骨骼结构作为传声介…

mysql、MHA高可用配置即故障切换

MHA概述 一套优秀的MySQL高可用环境下故障切换和主从复制的软件 MHA的出现就是解决MySQL 单点的问题 MySQL故障过程中,MHA能做到0-30秒内自动完成故障切换 MHA能在故障切换的过程中最大程度上保证数据的一致性以达到真正意义上的高可用 MHA的组成(核…

智能手机收入和出货量双双下滑,造车成本不断增长,小米集团仍面临风险

来源:猛兽财经 作者:猛兽财经 华尔街分析师对小米集团第二季度的业绩预测 在8月29日小米集团(01810)公布其2023年第二季度财报之前,华尔街分析师曾预测该公司第二季度的业绩将超出2023年第一季度的业绩。 根据S&P …

SpringBoot2.0(Spring读取配置文件常用方法,打war包在Tomcat中启动)

目录 一,SpringBoot中读取配置文件的常用方法1.1,使用Value读取1.2,使用ConfigurationProperties1.3,使用Environment1.4,自定义配置文件读取 二,SpringBoot部署war项目到tomcat9和启动原理 一,…

RabbitMq消息模型-队列消息

队列消息分为2种: 基本模型(SimpleQueue)、工作模型(WorkQueue) 队列消息特点: 消息不会丢失 并且 有先进先出的顺序。消息接收是有顺序的,不是随机的,仅有一个消费者能拿到数据&…

代码随想录算法训练营第五十六天|583. 两个字符串的删除操作、72. 编辑距离、编辑距离总结篇

583. 两个字符串的删除操作 文档讲解 : 代码随想录 - 583. 两个字符串的删除操作 状态:再次回顾。 动态规划五部曲: 确定dp数组(dp table)以及下标的含义 dp[i][j]:以i-1为结尾的字符串word1,和…

肖特基二极管SBD,SOD-123封装有哪些型号?

肖特基二极管,常用二极管之一,对于电子工程师来说,并不陌生。肖特基二极管,又称肖特基势垒二极管、热载流子二极管,英文Schottky Barrier Diode,缩写SBD,是利用金属-半导体(M-S)接触特性制成&am…

C语言:扫雷小游戏

文接上一篇博文C语言:三子棋小游戏。本篇博文是使用C语言来实现扫雷小游戏的。这里不对扫雷的规则进行赘述。玩家通过键盘输入坐标来探雷。博主在实现扫雷之前从未看过扫雷实现的相关视频,所以这里实现的扫雷完全是博主的原生思路,具有逻辑性…

用python开发一个炸金花小游戏

众所周知扑克牌可谓是居家旅行、桌面交友的必备道具, 今天我们用 Python 来实现一个类似炸金花的扑克牌小游戏,先来看一下基本的游戏规则。 炸(诈)金花又叫三张牌,是在全国广泛流传的一种民间多人纸牌游戏…

乐信仍面临资产质量下降和拖欠率上升风险

来源:猛兽财经 作者:猛兽财经 公司介绍 乐信(LX)成立于2013年10月,是中国领先的新消费数字科技服务商。旗下业务包括线上分期购物商城分期乐,全场景信用消费产品乐花卡,新型分期购物平台买吖,助力金融机构…

广告、政府、IT三重合作:凭爱校对轻松搞定文本质量

在广告创意、政府政策和IT开发这三个看似不相关的领域中,有一个共同的需求:高质量的文本内容。本文将探讨如何通过使用“爱校对”工具,在这三个行业内确保文本质量,从而提高工作效率和准确性。 广告行业:语境与创意的完…

EDM邮件营销:使用EDM代发实现更高发送率

虽然现在进入数字时代,但电子邮件依然是企业跟客户之间沟通最有效的方式之一。为了吸引并且留存目标用户,各大企业都在努力做好EDM(Electronic Direct Mail)邮件营销。但是通常用电子邮箱发送外贸邮件会有发送数量和自动化的限制&…

固定资产管理怎么写报告

撰写固定资产管理报告时,需要考虑以下几个维度的数据:  资产总量和分类:列出公司的固定资产总量、种类以及各类型资产的数量。  资产使用情况:统计各类型资产的使用率、闲置率、报废率等数据,以及不同部门的资产使…

西电Latex毕业模板使用时的小技巧

西电Latex毕业模板 配置的环境:textlivetextstudio \qqad 空格 参考文献先设置成bib,放到tex文件下,然后如下操作就可以将参考文献加载进去 如果搜不到相关文献的bib格式,可以用zotero软件将下载好的文件导出为bib格式&#xf…

43、Flink之Hive 读写及详细验证示例

Flink 系列文章 1、Flink 部署、概念介绍、source、transformation、sink使用示例、四大基石介绍和示例等系列综合文章链接 13、Flink 的table api与sql的基本概念、通用api介绍及入门示例 14、Flink 的table api与sql之数据类型: 内置数据类型以及它们的属性 15、Flink 的ta…

在线客服如何与客户进行有效沟通?

在今天的“互联网”时代,越来越多的服务都开始向线上转移,其中最受欢迎的莫过于在线客服。在线客服不仅可以提供7x24小时的在线咨询服务,还可以提高企业的服务效率和满意度。然而,有时候在线客服与客户之间的沟通效果却不太令人满…

EMERSON A6500-CC 机架接口模块 AMS参数

EMERSON A6500-CC 机架接口模块 AMS参数 ModBus和机架接口模块设计用于工厂的高可靠性 最关键的旋转机械。它从所有AMS A6500 ATG模块读取参数 并通过ModBus TCP/IP和/或ModBus RTU(串行)输出这些参数。 此外,OPC UA可用于向第三方系统传输数…

sentinel1.8.6中的blockHandler/blockHandlerClass和fallback/fallbackClass

官网介绍 简单的说blockHandler/blockHandlerClass是给限流降级用的,异常为BlockException,fallback/fallbackClass是给除BlockException之外的业务异常兜底用的。 官方文档还说明。1.6.0 之前的版本 fallback 函数只针对降级异常(DegradeEx…

Leetcode125. 验证回文串

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。 字母和数字都属于字母数字字符。 给你一个字符串 s&…

软件测试/测试开发丨Web自动化测试 关键数据记录

点此获取更多相关资料 本文为霍格沃兹测试开发学社学员学习笔记分享 原文链接:https://ceshiren.com/t/topic/27105 记录关键数据的作用 内容作用日志1、记录代码执行情况,方便复现场景,也可以作为bug依据截图1、断言失败或成功的截图&#…