辨别代码能否引发线程安全问题--避免在平时写代码时引发线程安全问题

news2025/7/19 3:48:02

前景提要: 本篇文章只是入门,目的在于在脑海中构建一个Java运行的模型,然后可以在平时写代码时对是否引发线程安全问题有感知。

文章目录

  • 引入
    • 了解辨别线程安全问题之前先来构建一个计算机运行模型
    • 了解线程安全问题怎么能不知道线程和进程
      • 了解进程和线程的概念
      • 线程和进程的抽象化理解
    • Java代码运行的模型JVM--Java的平台无关性
      • 了解CPU不同种类有不同指令集
      • java的平台无关性
  • 分析并发性问题
    • java代码执行所依赖的核心区域--通过区域来分析并发问题
      • JVM内存区域
    • 基本数据类型和引用数据类型--基本和引用数据类型的线程安全问题
      • 分析基本数据类型和引用数据类型的传参
      • 基本数据类型和引用数据类型的线程安全分析
    • 通过对象的使用--分析产生的并发问题
    • 对于成员变量的线程安全分析--依赖于父元素
    • 对于静态变量(类变量)
  • 总结

引入

了解辨别线程安全问题之前先来构建一个计算机运行模型

可以将计算机简单看作两部分:

  • CPU
  • 存放数据和指令的地方

在这里插入图片描述

计算机运行

  • CPU 执行一条条指令,每条指令完成对数据的操控例如move(移动,一个数据移动到另一个地方)…
    这样计算机成功运行

计算机有多个任务怎么处理

  • 首先假设是一个一个处理,在一个任务中有普通指令和IO指令,IO指令是非常慢的,因为要去磁盘读取数据,执行IO时CPU是不工作的在等待,第一个任务遇到IO指令等待,第二个任务也等待…如果将等待IO的时间去执行其他任务这样会快很多。
  • 实际上同时有多任务时,并不是将一个任务执行完,再去执行另一个,而是有一个调度系统,对每个任务分一个时间片,在时间内执行这个任务,然后再执行其他任务,这其中有一个任务切换叫做上下文切换,上下文切换切换是由需要消耗时间的

了解线程安全问题怎么能不知道线程和进程

了解进程和线程的概念

什么是进程?

  • 进程,顾名思义是一段进行中的程序,上述提到CPU是不断切换执行任务的,对于这种执行到一定部分的程序叫做进程。
  • 进程,是上述计算机中执行的一个个任务,由于CPU有多核,可以同时运行多个任务,也就是多个进程。

什么是线程?

  • 进程是一个大任务,这个大任务想要完成就要一点点的去做,分成一个个小任务,比如要建一个房子,先要打地基,然后一层层盖,最后刷漆装修等。进程这个大任务包含的一个个小任务就是是线程

线程和进程的抽象化理解

在这里插入图片描述

  • 进程可以看作一家公司,将任务分配给公司,公司是死的没办法执行任务,具体做事情的是一个个员工,这些员工就是线程
  • 线程是调度的基本单位,当分给公司一个任务的时候,同时也会分完成任务所需要的资源,所以进程是资源分配的基本单位,同时各个线 程去共享进程的资源。员工完成各自的任务需要公司的资源,比如说打印机,员工之间共享使用。
  • 总的来说,进程可以看做是一个大的线程组,进程是不做事的,事情交给进程去做

Java代码运行的模型JVM–Java的平台无关性

了解CPU不同种类有不同指令集

  • CPU 有很多种类如AMD和Intel,这两种CPU在指令集方面是有不同的,不同的CPU厂商有一天套不是很相同的指令集,相同指令集在硬件上有不同实现

  • CPU就是去执行一条条指令(指令是机器码是一串二进制数字。指令就是我们常说的机器语言,机器语言(machine language)是一种指令集的体系。这种指令集,称机器码(machine code),是电脑的CPU可直接解读的数据)。

  • 计算机去识别机器语言或汇编语言去执行一条条指令。汇编语言是人类看的懂的语言来描述指令集,汇编语言操作起来还是非常困难的,人类又发明了高级语言比较贴合人类的语言更容易理解,如C,C++,java,但是计算机看不懂高级语言的,c,c++要转化(通过编译器编译)成机器语言。

  • 机器语言、汇编语言、高级语言的联系:
    高级语言要通过编译程序翻译成汇编代码,汇编语言要通过汇编得到机器语言,计算机才能执行

  • C或C++语言可能会因为CPU等的差异会运行成不同的结果
    最常见的例子是c语言中int在32,与64位下表示的范围不同。所以c,c++依赖于硬件,在不同计算机运行结果可能不同。
    原因是因为C或C++会转化成机器语言,通过CPU执行,由于机器语言在不同CPU会有差异。导致C或C++在不同计算机上有差异。

java的平台无关性

Java并不是直接编译成机器语言,java通过编译器编译成字节码
通过虚拟器执行字节码,这样就会不依赖于硬件,依赖于虚拟机
不同的计算机,不同的操作系统装不同的虚拟机,具体的硬件差异通过虚拟机来解决,从而达到相同的执行效果,这就是java的平台无关性

分析并发性问题

java代码执行所依赖的核心区域–通过区域来分析并发问题

JVM内存区域

在这里插入图片描述

  • JVM虚拟机运行起来的时候是一个进程
  • JVM内存主要分为5部分,堆和方法区是共享的,其他的三部分是线程私有的
  • ,栈对应了方法,执行一个方法时,会形成一个栈帧放进栈中,栈帧里存放的是方法中的局部变量数据。方法执行开始到执行完毕对应了栈帧入栈到出栈的过程。递归和方法调用也是借助了栈。栈是线程私有的,其中方法内的数据也是私有的
  • ,存放对象数据的地方。这个是共享的

在这里插入图片描述

  • 大多数对象的数据放在堆里,对象的引用,如果是在方法内是放在栈里,方法外放在堆里
  • 栈是私有的如果放在方法里这个对象是线程安全的(排除对象作为参数和返回值引发的线程安全问题),如果放在方法外可能不是线程安全的
  • 引用放在什么地方,放在方法里,就是将以用放在私有的栈中,这就是线程安全的,在方法内是线程安全的,除非将这个引用交给其他方法,或从其他地方拿过来的,不能保证在其他地方线程安全,对应了引用为参数和返回值的情况。这是局部变量和方法内地引用
    总的来说,线程安不安全主要看是引用放在了什么位置,放在栈是线程安全的,放在堆是线程不安全的

基本数据类型和引用数据类型–基本和引用数据类型的线程安全问题

分析基本数据类型和引用数据类型的传参

  • 基本数据类型存放的是本身的值
    例如,i这个变量中放的就是10
int i = 10;

在这里插入图片描述

  • 对于引用数据类型,放的是对象数据的地址,通过这个地址来访问对象数据
    在这里插入图片描述

  • 基本数据类型传参
    通过调用swap(i,j)方法并不会让i,j值产生交换
    原因是传参是复制的值,复制完以后成为了两个独立个体。

public void swap(int a,int b )
{

int t=a;
a=b;
b=t;
}
  • 对于引用数据类型是将地址复制给对方。对方可以通过地址访问对象数据,会真正改变值
public void swap(Integer a,Integer  b )
{

int  t=a;
a=b;
b=t;
}

基本数据类型和引用数据类型的线程安全分析

  • 对于基本数据,不管是在方法内部还是作为参数,都是线程安全的。作为参数时,在传参的过程中,直接将值赋值给参数,方法的参数丝毫不会影响外部的变量
    在这里插入图片描述

  • 对于引用数据类型,作为传参和返回值的时候,传递的是地址,每个拿到地址的变量,都可随意更改。总的来说,作为传参和返回值时,线程安全取决于拿到地址的那些变量是否有线程安全问题,如果只在方法内部使用,一定线程安全
    在这里插入图片描述

  • 也就是说把基本数据类型,放在方法里是绝对安全的,但是要区分传参使用,和直接使用成员变量这两种情况。,

通过对象的使用–分析产生的并发问题

  • 上面通过对私有区域栈和共享区域堆的分析可以确定如果放在方法里这个对象是线程安全的(排除对象作为参数和返回值引发的线程安全问题),现在通过对对象的引用来进一步分析线程安全问题

  • 对一个对象的抽象理解
    在这里插入图片描述

  • 房子的角度来考虑,我是房屋主人我只有一把钥匙,谁也不给,这个房子是只有我自己能进,这很明显是安全
    如果我将钥匙复制了很多份给了其他人,或者我的钥匙被坏人偷偷复制了一份,那么此时我的房子是不安全的

  • 分辨是否会引发线程安全问题主要是看引用,这个对象的引用是否是被其他人拿到,是那就不安全,否就是私有就是安全
    举个例子:

public  List test(List list)
{

方法内的具体逻辑操作
return list;
}

如果引用放在堆里,堆是共享的,所以是不安全的。这是类变量(静态),和成员变量的情况。

基本数据类型存放的是值,引用数据类型存放的是地址(根据这个地址可以找到对象数据),对象绝大多数放在堆里。
对应java是不能直接操作地址的。对于方法之间的传参,引用数据类型,相当于是一把钥匙,通过参数形式传入方法,相当于是复制了一把钥匙交给对方,对方也有了操作的权限。对于基本数据类型,方法传参,像当于直接克隆了一个崭新的给对方。在方法内操作基本数据类型是不会影响到方法外的基本数据类型的。
对于方法内的基本数据类型,可以说都是线程安全的,但是要区别下面两种情况
提一点:
对于成员变量和类变量是不分基本数据类型和引用数据类型的,这两个是否引发线程安全问题情况是一样的

对于成员变量的线程安全分析–依赖于父元素

Class Main{

private int i;

public void test(int a)
{

a++;
}
}

调用test(i)是线程安全的

Class Main{

private int i;

public void test()
{

i++;
}
}

这个是线程不安全的

从引用数据类型类比为钥匙的角度谈线程安全性:
对于方法内的局部变量,这个局部变量是内部定义的,又 不会当作返回值,这就是线程安全的。如果局部变量是参数的话,或者是返回值,相当于从别人手中拿到钥匙,或把钥匙给了别人,这就依赖于其他地方是不是线程安全的。
对于成员变量,引用是放在堆里的,他的线程安全性依赖于引用他的父元素,对于成员变量实例化后才能操作,这个线程安全性使用它的父元素,如果这个父元素只是自己使用是没有线程安全的,如果把这个引用交给多个人,就可能引发线程安全问题
举个例子: 经典的 模拟抢票

Class Tacket

{
//票的数量
private int tacket=0;

//抢票方法
public void  qTacket()
{
if(tacket>=0)
tacket--;
}

}

// class MyThread extend Thread
{

private Tacker tacket;
public MyThread ()
{
super();
}
public MyThread(Tacket tacket)
{
this .tacket=tacker;

}
//重写run方法,run()方法内部是执行抢票的动作
public void run()


{
tacket.qTacket();

}

}


//开始模拟抢票过程

Class Main{

public static void main(String []args)
{
//拿到操作票的钥匙
Tacket tacket=new Tacket();
//开启10个线程去抢票,相当于把钥匙给了很多人,造成线程不安全
for(int i=0;i<10;i++)
{
new MyThread(tacket).start();
}
}

}

解释:

  • 对于Tacket这个类来说,成员变量tacket无法看出是否线程安全,但是在Main类中,将tacket对象交给多个线程,在每个线程中对成员进行了读写操作,引发了线程安全问题。
  • 所以,对于成员变量的线程安全性分析,主要看父类元素是否线程安全

对于静态变量(类变量)

  • 静态变量,不管在哪个线程,在哪个地方,可以通过方法名.变量名来访问静态变量
  • 这种情况下,如果只读这并不会引发线程安全问题,
  • 但是再实际过程中,绝大部分都是读写操作,这就会引发线程安全问题了
  • 所以,对于静态变量,在多线程开发环境中,一定是需要去手动保证线程安全的

总结

总之,线程安全问题最终还是会回到是否共享的,但是是否共享是一个很宽泛的概念,包含了很多情况,在很多时候可能并不会注意到,通过这篇文章呢可以帮助我们在平时写程序的时候对于线程安全问题有明显的感知。

在这里插入图片描述

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

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

相关文章

搭建repo服务器管理多个git工程

参考自&#xff1a;搭建repo服务器管理多个git工程     repo系列讲解 —— Android系统源码(AOSP)下载 1、repo介绍 Android使用git作为代码管理工具&#xff0c;开发了gerrit进行代码审核&#xff0c;以便更好的对代码进行集中式管理。还开发了repo命令行工具&#xff0…

MySQL8.0 binlog进阶

MySQL8.0经过这几年的操揉磨治&#xff0c;已经上升到海平面了。其中binlog也悄然无声带来了不一样的变化。高可用核心复制基础binlog变化更应该进一步了解。从参数入手&#xff0c;了解带来的变化。 slave回放算法 slave_rows_search_algorithms 当使用基于 row-based复制格…

牛客网语法篇练习循环控制(二)

1.今天牛牛学到了回文串&#xff0c;他想在数字里面找回文&#xff0c;即回文数&#xff0c;回文数是正着读与倒着读都一样的数&#xff0c;比如1221&#xff0c;343是回文数&#xff0c;433不是回文数。请输出不超过n的回文数。 a int(input()) for i in range(1,a1):n str…

CSS-counter 计数器详细教程+使用场景示例

counter一. counter计数器二. 属性和方法1. 计数器命名/重置2. 计数器-值递增规则3. 计数器显示 counter() / counters() 函数三 代码示例1. 重新开始计数2. counters嵌套使用3. 借助CSS计数器呈现CSS var变量值一. counter计数器 计数器是一种特殊的数字跟踪器&#xff0c;通常…

systemd的unit配置文件详解

Systemd 是 Linux 的系统和服务的管理器&#xff0c;兼容 SysV 和 LSB初始化脚本&#xff0c;Systemd有以下特性&#xff1a; 积极的并行化能力使用套接字和 D-Bus 激活来启动服务提供按需启动守护进程&#xff0c;使用 Linux cgroups 跟踪进程支持系统状态的快照和恢复维护挂…

艾美捷Cas9核酸酶应用说明及实例展示

Product Description:Recombinant Streptococcus pyogenes Cas9 (wt) protein expressed in an E. coli . Form:Liquid Preparation Method:E. coli expression system Purity:≥ 95% by SDS-PAGE Activity:20 nM CRISPR/Cas9-C-NLS nuclease incubated for 1 hour at 37℃…

【新知实验室 TRTCIM】实时互动课堂最佳实践

【新知实验室 TRTC&IM】实时互动课堂最佳实践一、新知实验室-TRTC腾讯云音视频产品体验官计划活动简介二、产品简介TRTCIM三、最佳实践3.1 官方快速上手TRTC(快速跑通)3.1.1 注册腾讯云账号3.1.2 使用实时音视频(需先开通)3.1.3 创建应用3.1.4 查看项目(查看密钥和快速上手…

java基础—String

我们都知道 创建一个字符串最简单的方式是 String meaasge "java资讯";当然还可以用构造来创建 &#xff08;不推荐&#xff0c;开发中不要用&#xff09; String str2new String("java资讯");这两种创建最主要的区别在于&#xff0c;一个在公共池中&…

gitlab CI/CD 自动化部署vue项目到阿里云服务器步骤

目录1&#xff0c;gitlab托管vue项目2&#xff0c;本地项目连接到远程仓库3&#xff0c;设置gitlab-runner4&#xff0c;编写yml文件5&#xff0c;部署到阿里云服务器&#xff08;本地设置&#xff09;5.1 安装相关依赖5.2 vue项目中添加deploy.js文件5.3 注册deploy命令5.4 验…

文本生成图像工作简述2--常用数据集分析与汇总

文本到图像的 AI 模型仅根据简单的文字输入就可以生成图像。用户可以输入他们喜欢的任何文字提示——比如&#xff0c;“一只可爱的柯基犬住在一个用寿司做的房子里”——然后&#xff0c;人工智能就像施了魔法一样&#xff0c;会产生相应的图像。 文本生成图像&#xff08;te…

实验2:Arduino的nRF24L01双向收发实验

实验结果: 00节点向01发送:00ReqMesFor01 01节点向00发送:CodeNewNiceBoy 并且在串口打印出相应信息 硬件电路: 01 软件 00节点代码: /*00 */#include <SPI.h> #include <nRF24L01.h> #include <RF24.h> RF24 radio(9, 10);// CE, CSNconst char te…

dolphinscheduler 2.0.5 性能手动测试

目录&#x1f42c;官方配置文件说明&#x1f42c;测试并发量&#x1f420;线程数量设置100&#x1f420;线程数量设置200&#x1f420;线程数量设置500&#x1f42c;测试结论&#x1f42c;官方配置文件说明 官方说明 master.exec.threads&#xff1a; master工作线程数量,用于…

智能晾衣架(二)--功能实现

本文素材来源于红河学院 工学院 作者&#xff1a;赵德森 张艺锦 潘志慧 曹紫康 指导老师&#xff1a;江洁 张龙超 1. 自动升降功能 我们设计时采用了热释电传感器&#xff08;人体红外传感器&#xff09;&#xff0c;在热释电传感器感应到有人靠近时&#xff0c;晾衣架通…

C++:内存管理:C++内存管理详解(二):带你攻破内存管理

前言&#xff1a; 任何程序运行起来都需要分配内存空间存放该进程的资源信息&#xff0c;C程序也不例外。C程序中的变量、常量、函数、代码等等信息所存放的区域都有所不同&#xff0c;不同的区域又有不同的特性。 欺骗C进程 每一个C语言的程序被执行起来的时候系统为了方便开…

字符串的简单介绍和字符串的大小比较

以前就写过一篇关于String的文章&#xff0c;今天再来写一篇&#xff0c;更加深入了解一下String类 &#x1f550;1.String类的定义 &#x1f551;2.String类的创建 &#x1f552;3.字符串的大小比较 1.之前在C语言中我们已经学到了字符类型&#xff0c;但是C语言没有Strin…

Oracle-Rman duplicate文件坏块问题处理ORA-19849 19612

前言: 最近&#xff0c;在使用rman duplicate进行备库环境搭建时&#xff0c;遇到了ORA-19849 19612坏块报错&#xff0c;最终分析是发现由于网络的配置导致。 问题: 在 ORACLE 12.2.0.1.180417 通过RMAN duplicate进行备库初始化&#xff0c;在复制文件的过程中&#xff0c;…

FTP服务器移植到Linux开发板

FTP服务器移植到Linux开发板 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录FTP服务器移植到Linux开发板前言一、vsftpd源码下载二、vsftpd移植及配置1.vsftpd移植2.vsftpd配置2.1 配置vsftpd.conf2.2 添加新用户2…

LVS+Keepalived群集

目录 一.keeepalived工具介绍 1.1 专为LVS和HA设计的一款健康检查工具 1.2 Keepalived实现原理剖析 1.3 VRRP工作过程 1.4 Keepalived&#xff0c;VRRP及其工作原理 1.5 Keepalived体系主要模块及其作用 二、LVSKeepalived 高可用群集的搭建步骤 1、配置负载调度器&…

项目管理(PMO的作用)

项目管理办公室 (PMO) 是对与项目相关的治理过程进行标准化&#xff0c;并促进资源、方法论、工具和技 术共享的一个组织结构。PMO 的职责范围可大可小&#xff0c;从提供项目管理支持服务&#xff0c;到直接管理一个 或多个项目。 PMO的三种类型&#xff1a; PMO的作用&#…

ppt背景图片怎么设置?6步教你快速搞定!

在制作 ppt的过程中&#xff0c;需要将一些内容进行设置&#xff0c;来保证整体美观&#xff0c;也就是将背景图片设置好。背景图片设置的好&#xff0c;可以很好地体现页面上的内容&#xff0c;提高页面上的内容展示效果。所以大家在制作 ppt时&#xff0c;需要将一些背景图片…