当前位置: 首页 > news >正文

java并发编程之基础与原理1

java多线程基础 

 下面说一下线程的7种状态

下面我重点来说一下阻塞状态

阻塞状态是可以分很多种的:

下面用另外一张图来说明这种状态

 简单说一下线程的启动原理

下面说一下java中的线程

 

java线程的异步请求方式

上面就会先把main执行出来,等阻塞结束之后把run()方法里面的come in执行出来,这个是一个异步的操作

从线程中取得一个返回值

 1.用一个类去实现Callable接口

 上面可以返回一个结果

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;// 实现Callable接口,指定返回值类型为String
public class CallableDemo implements Callable<String> {// 实现call方法,该方法内为具体的任务逻辑@Overridepublic String call() throws Exception {System.out.println("come in"); // 输出提示信息Thread.sleep(10000); // 休眠10秒,模拟耗时操作return "SUCCESS"; // 返回任务执行结果}// 主函数public static void main(String[] args) throws ExecutionException, InterruptedException {// 创建一个固定大小为1的线程池ExecutorService executorService = Executors.newFixedThreadPool(1);// 创建CallableDemo实例CallableDemo callableDemo = new CallableDemo();// 提交任务给线程池,获得一个Future对象Future<String> future = executorService.submit(callableDemo);System.out.println(future.get()); // 阻塞,等待任务执行完毕并获取结果executorService.shutdown(); // 关闭线程池}
}

我们把上面的线程交给线程池去执行,然后返回一个Future对象接收返回值,里面说一下future.get()方法是一个阻塞方法,比如上面线程阻塞了10秒钟,我们get()这个方法是拿不到结果的

interrupt()的作用

上面的运行情况下面分析一下:

        在某些情况下,在线程中断之前,可能不会打印消息“Test:1”。这是因为线程调度程序决定何时在线程之间切换,并且新线程可能在有机会打印消息之前就被中断了。start()方法和interrupt()方法之间的执行顺序没有保证,因此中断可能在线程有机会开始执行其run()方法之前发生。

下面我们来看另外一段代码

package com.pxx.interrupt;public class InterruptDemo2 implements Runnable {@Overridepublic void run() {while (!Thread.currentThread().isInterrupted()) {try {Thread.sleep(200);//这里睡了0.2秒System.out.println("interrupt线程运行了");} catch (InterruptedException e) {//这里会有复位的作用,又把isInterrupted变为falseSystem.out.println("异常被捕获");e.printStackTrace();}}}public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(new InterruptDemo2());t1.start();Thread.sleep(1000);t1.interrupt();//把isInterrupted()这个方法变为trueSystem.out.println(Thread.currentThread().getName() + "执行了");}}

  我们来看其中的一个运行结果:

那么我们来分析一下运行结果

我先来说一下InterruptedException这个异常会在什么时候去触发,它的触发条件是

        !Thread.currentThread().isInterrupted()这个位置变为true的时候,它就会去触发这个异常

并且又把这个异常复位为false

上面就是main主线程调用了start之后,就被t1线程抢走了并开始执行,然后中间main线程抢回了执行的时间片,把isInterrupted变为了true,这个时候t1线程又抢走了时间片,发现iisInterrupted()这个方法变为了true,于是触发了异常,并且复位了isInterrupted()变为了false,然后开始执行catch里面的代码,执行完一句之后,又被main线程抢走了,这个时候main执行完最后一条语句结束,然后t1去执行打印异常栈追踪信息,又开始一直循环执行里面的代码,因为没有在让isInterrupted()继续中断的条件

好了我们可以改动一下上面的代码让它继续中断

说一下并发与并行 

并发是并行的假象,看似程序在同时执行多个操作,而并发只是要求程序假装同时执行多个操作,也就是每个时间片执行一个操作,多个操作快速切换执行而已

大体说一下并发编程的三大特性 

可见性 

当一个线程修改了共享变量的值,其他线程能够看到修改的值。

具体原理分析如下:

当我们在Java程序中定义了一个变量,例如 int x = 5;,这个变量实际上在计算机内存中有两个位置保存着它的值:一个是主内存(主存,RAM),另一个是线程的工作内存(缓存,CPU缓存)。在不同的线程中,可能会有各自的工作内存。

当一个线程修改了这个变量的值,例如 x = 10;,这个修改首先会发生在线程的工作内存中,而不是直接在主内存中。其他线程如果要读取这个变量的值,通常会从主内存中读取。问题在于,由于工作内存的存在,不同的线程可能在各自的工作内存中保存了不同的变量值。

为了确保多个线程之间对共享变量的修改能够正确地被其他线程看到,Java 内存模型使用了“主内存同步”的机制。当一个线程修改了变量的值后,会将新值同步回主内存,而其他线程在读取这个变量的值之前,会先从主内存中刷新(获取)变量的最新值,而不是直接从自己的工作内存中读取。

下面说的直白一点就是:

 

有序性

 

简单说一下什么叫指令重排

指令重排是指在程序执行过程中,CPU或者编译器为了提高性能,可能会对指令的执行顺序进行优化,使得程序在逻辑上的执行顺序与实际的指令执行顺序不一致。

在Java中,由于Java代码最终会被编译成字节码,然后由JVM执行,JVM为了提高程序执行的效率,也可能会进行指令重排。这就意味着,即使程序的源代码中顺序是有序的,JVM在执行时可能会重新排列指令的执行顺序。

指令重排可能会导致多线程程序出现问题。例如,如果一个线程在初始化一个对象时,先为对象分配内存空间,然后初始化对象的属性,最后将对象的引用赋值给某个变量。如果发生了指令重排,可能会导致另一个线程在获取到对象的引用之后,访问到的对象并未完成初始化,从而引发错误。

 

 原子性

        一个或多个操作,要么全部执行且在执行过程中不被任何因素打断,要么全部不执行

来说一下JMM内存模型

Java内存模型(Java Memory Model,JMM)

首先要明白JMM内存模型他不是一个具体硬件存在,它是一个虚拟的概念。

了解了上面这个概念之后,我们必须去了解一下计算机内部的重要组成部分以及jvm到底占据计算机什么位置和内存是如何划分的

直接用一张图来说明一下

好了,在回过头来说JMM,它是一个概念,一个数据的处理规则对吧,下面我们就来剖析一下这个规则

JMM规范了Java虚拟机与计算机内存是如何协同工作的:

规定了一个线程如何和何时可以看到由其他线程修改过后的共享变量的值,以及在必须时如何同步的访问共享变量。它是围绕原子性,有序性,可见性展开的

单纯这样来看,我们能看懂个屁啊,既然是围绕可见性展开的,那我们就贴一段会有可见性问题的代码来说一下

package com.pxx.visibility;public class VisibilityTest {private boolean flag = true;public void refresh() {flag = false;System.out.println(Thread.currentThread().getName() + "修改flag");}public void load() {System.out.println(Thread.currentThread().getName() + "开始执行...");int i = 0;//进行循环,flag = truewhile (flag) {i++;}System.out.println(Thread.currentThread().getName() + "跳出循环 i=" + i);}public static void main(String[] args) throws InterruptedException {VisibilityTest test = new VisibilityTest();//线程threadA模拟数据加载场景Thread threadA = new Thread(() -> test.load(), "threadA");threadA.start();//让threadA执行Thread.sleep(1000);Thread threadB = new Thread(() -> test.refresh(), "threadB");threadB.start();}}

运行结果:

分析可见性:

 下面在用一个图来讲解一下JMM概念

 上面还提到了主内存与工作内存的具体的交互操作:

 

下面我来说一个问题,我刚刚提到,只要本地内存共享变量一直存在,那么程序就会从本地缓存里面取数据,那么什么时候本地内存会不存在呢?

第一,栈空间是有限的,到了一定的限度之后,会把变量给清理掉

第二,隔了一段时间本地变量一直没有被使用,也会清理掉

上面两种线程在运行的时候,发现本地没有共享变量了之后,都会去主内存从新加载变量到本地内存。

下面可以看一个代码实例

这个方法是让线程能隔一段纳秒的时间在运行,纳秒的原因是我们可以把时间控制的非常短

 上面就可能会让flag失效,从而去主内存里面获取最新的数据,然后跳出循环

下面讲一个Thread.yield(),他会释放当前线程的时间片,让当前线程进入一个可运行状态,并且保存数据,下次这个线程在次执行的时候,如果发现主内存已经修改了某个共享变量,就会从主内存去获取这个共享变量的值

为什么volatile也可以跳出循环

 我们去看一下jvm中的字节码解释器源码bytecodeInterpreter.cpp

 

然后内部去调用了一个内存屏障处理

 

 x86处理器中利用lock实现类似内存屏障的效果。

lock前缀指令的作用

会等待它之前所有指令完成,并且所有缓冲的写操作写回内存,也就是将store buffer中的内容写入内存之后才开始执行

lock会立即把本地内存修改的变量刷新到主内存里面,同时会让其他处理器中的本地内存的缓存副本失效,它失效,然后又会从主内存读取共享变量

他不是一个内存屏障的指令,但是它有内存屏障的效果

volatile的本质:

下面这个方法

UnsafeFactory.getUnsafe().storeFence();

核心也是

//能够跳出循环    内存屏障
 //System.out.println(count);

//LockSupport.unpark(Thread.currentThread());

上面都是调用内存屏障

还有就是Thread.sleep(1)这种操作也是调用了内存屏障

定义为final也会保证某个变量的可见性

 

相关文章:

java并发编程之基础与原理1

java多线程基础 下面说一下线程的7种状态 下面我重点来说一下阻塞状态 阻塞状态是可以分很多种的&#xff1a; 下面用另外一张图来说明这种状态 简单说一下线程的启动原理 下面说一下java中的线程 java线程的异步请求方式 上面就会先把main执行出来&#xff0c;等阻塞结束之后…...

⟨A⟩ = Tr(ρA) 从数学上来讲什么意思

当给定一个具体的密度矩阵ρ和一个可观测量A时&#xff0c;我们可以通过数值计算来演示〈A〉 Tr(ρA) 的应用。 假设我们有以下密度矩阵和可观测量&#xff1a; ρ [0.6 0.3; 0.3 0.4] A [1 0; 0 -1] 我们首先计算ρA的乘积&#xff1a; ρA [0.6 0.3; 0.3 0.4] * [1 0…...

Vue中的v-model指令的原理是什么?

在Vue中&#xff0c;v-model是一个双向绑定指令&#xff0c;它的原理是将表单元素的值与Vue实例中的数据属性进行双向绑定。当表单元素的值发生变化时&#xff0c;会自动更新Vue实例中对应的数据属性&#xff1b;反之&#xff0c;当Vue实例中的数据属性发生变化时&#xff0c;也…...

2023服务端测试开发必备技能:Mock测试

什么是mock测试 Mock 测试就是在测试活动中&#xff0c;对于某些不容易构造或者不容易获取的数据/场景&#xff0c;用一个Mock对象来创建以便测试的测试方法。 Mock测试常见场景 无法控制第三方系统接口的返回&#xff0c;返回的数据不满足要求依赖的接口还未开发完成&#…...

ExoPlayer架构详解与源码分析(5)——MediaSource

系列文章目录 ExoPlayer架构详解与源码分析&#xff08;1&#xff09;——前言 ExoPlayer架构详解与源码分析&#xff08;2&#xff09;——Player ExoPlayer架构详解与源码分析&#xff08;3&#xff09;——Timeline ExoPlayer架构详解与源码分析&#xff08;4&#xff09;—…...

控制一个游戏对象的旋转和相机的缩放

介绍 这段代码是一个Unity游戏开发脚本&#xff0c;它用于控制一个游戏对象的旋转和相机的缩放。以下是代码的主要功能&#xff1a; 控制游戏对象的旋转&#xff1a; 通过按下Q键和W键&#xff0c;用户可以选择以逆时针或顺时针方向绕游戏对象的Y轴进行旋转。旋转角度和速度可…...

【数据结构】线性表(二)单链表及其基本操作(创建、插入、删除、修改、遍历打印)

目录 前文、线性表的定义及其基本操作&#xff08;顺序表插入、删除、查找、修改&#xff09; 四、线性表的链接存储结构 1. 单链表&#xff08;C语言&#xff09; a. 链表节点结构 b. 创建新节点 c. 在链表末尾插入新节点 d. 删除指定节点 e. 修改指定节点的数据 f. …...

label的作用是什么?是怎么用的?(1)

Label&#xff08;标签&#xff09;在不同的上下文中有不同的作用和用途。以下是几种常见的用途和用法&#xff1a; 1. 数据标注&#xff1a;在机器学习和数据科学中&#xff0c;标签用于标识数据样本的类别或属性。标注数据是监督学习中的一项重要任务&#xff0c;它为算法提…...

C- 使用原子变量实现自旋锁

自旋锁 自旋锁&#xff08;Spinlock&#xff09;是一种常用于多线程编程中的低开销锁&#xff0c;其特点是当线程尝试获取锁而锁已被其他线程占用时&#xff0c;该线程会处于一个持续的忙等待&#xff08;busy-wait&#xff09;状态&#xff0c;直到它可以获取到锁为止。这种方…...

汇编的指令

减法类指令&#xff1a; 不带借位的减法&#xff1a; sub dest,src;dest(dest)-(src) 注意&#xff1a; 1、源操作数和目的操作数不能同时为段寄存器或存储单元 2、对标志位有影响&#xff0c;主要影响CF、ZF、OF、SF。 带借位的减法&#xff1a; sbb dest,src;dest(dest)-(…...

《数据结构、算法与应用C++语言描述》使用C++语言实现数组队列

《数据结构、算法与应用C语言描述》使用C语言实现数组队列 定义 队列的定义 队列&#xff08;queue&#xff09;是一个线性表&#xff0c;其插入和删除操作分别在表的不同端进行。插入元素的那一端称为队尾&#xff08;back或rear&#xff09;&#xff0c;删除元素的那一端称…...

零基础如何学习自动化测试

现在很多测试人员有些急于求成&#xff0c;没有任何基础想当然的&#xff0c;要在一周内上手自动化测试。 在自动化的过程中时候总有人会犯很低级的问题&#xff0c;有语法问题&#xff0c;有定位问题&#xff0c;而且有人居然连__init__.py 文件名都弄错误&#xff0c;还有将…...

系统架构师备考倒计时16天(每日知识点)

1.信息化战略与实施 2.UML图&#xff08;12个&#xff09; 3.结构化设计&#xff08;耦合&#xff09; 4.SMP与AMP的区别&#xff08;多核处理器的工作方式&#xff09; 多核处理器一般有SMP和AMP两种不同的工作方式: SMP(对称多处理技术)&#xff1a;将2颗完全一样的处理器封…...

【MySQL系列】- Select查询SQL执行过程详解

【MySQL系列】- Select查询SQL执行过程详解 文章目录 【MySQL系列】- Select查询SQL执行过程详解一、SQL查询语句的执行过程二、SQL执行过程详解2.1. 连接器2.2. 查询缓存2.3. 分析器2.4. 优化器2.5. 执行器 三、undo log 和 redo log作⽤3.1. redo log &#xff08;重做日志&a…...

软考高级信息系统项目管理师系列之:信息系统项目管理师论文评分参考标准

软考高级信息系统项目管理师系列之:信息系统项目管理师论文评分参考标准 论文满分是 75 分,论文评分可分为优良、及格与不及格 3 个档次。评分的分数可分为: 60 分至 75 分优良(相当于百分制 80 分至 100 分)。45 分至 59 分及格(相当于百分制 60 分至 79 分)。0 分至 44 分…...

MyBatis--多案例让你熟练使用CRUD操作

目录 一、前期准备 二、两种实现CRUD方式 三、增加数据&#xff08;INSERT&#xff09; 四、删除数据&#xff08;DELETE&#xff09; 五、查询数据&#xff08;SELECT&#xff09; 六、更新数据&#xff08;UPDATE&#xff09; 一、前期准备 1.创建maven项目并在pom文件…...

用Python造轮子

目录 背景安装setuptools库准备要打包的代码创建setup.py文件打包生成whl文件把库装到电脑上使用这个库 背景 如何把自己写的代码&#xff0c;打包成库方便其他人使用 安装setuptools库 正所谓想要富先修路&#xff0c;先把造轮子要用的库装上 pip install wheel pip insta…...

ARM 堆栈寻址类型区分

文章目录 堆栈指向分类堆栈指向数据分类满递增与满递减空递增与空递减 堆栈指向分类 根据堆栈指针的指向的方向不同&#xff0c;可以划分为向上生成型和向下生成型。 向上生成型&#xff1a; 随着数据的入栈&#xff0c;堆栈的指针逐渐增大&#xff0c;称为&#xff1a;递增…...

每日一练 | 网络工程师软考真题Day43

1、在生成树协议〔STP〕IEEE 802.1d中&#xff0c;根据 来选择根交换机。 A&#xff0e;最小的MAC地址 B&#xff0e;最大的MAC地址 C&#xff0e;最小的交换机ID D&#xff0e;最大的交换机ID 2、在快速以太网物理层标准中&#xff0c;使用两对5类无屏蔽双绞线的是 。 A&…...

jsonXML格式化核心代码

json格式化&#xff1a; 依赖&#xff1a; <dependency><groupId>com.jayway.jsonpath</groupId><artifactId>json-path</artifactId><version>2.6.0</version><scope>compile</scope> </dependency> string t…...

PTQ量化和QAT量化

目录 1--PTQ量化 2--QAT量化 1--PTQ量化 PTQ量化表示训练后量化&#xff08;Post Training Quantization&#xff09;。使用一批校准数据对训练好的模型进行校准&#xff0c;将训练好的FP32网络直接转换为定点计算的网络&#xff0c;过程中无需对原始模型进行任何训练&#x…...

【Django 02】数据表构建、数据迁移与管理

1. Django 构建数据表创建与数据迁移 1.1 数据表创建 1.1.1 模块功能 如前所述&#xff0c;models.py文件主要用一个 Python 类来描述数据表。运用这个类,可以通过简单的 Python 代码来创建、检索、更新、删除 数据库中的记录而无需写一条又一条的SQL语句。今天的例子就是在…...

一天吃透Java集合面试八股文

内容摘自我的学习网站&#xff1a;topjavaer.cn 常见的集合有哪些&#xff1f; Java集合类主要由两个接口Collection和Map派生出来的&#xff0c;Collection有三个子接口&#xff1a;List、Set、Queue。 Java集合框架图如下&#xff1a; List代表了有序可重复集合&#xff0c…...

高级深入--day36

Settings Scrapy设置(settings)提供了定制Scrapy组件的方法。可以控制包括核心(core),插件(extension),pipeline及spider组件。比如 设置Json Pipeliine、LOG_LEVEL等。 参考文档:Settings — Scrapy 1.0.5 文档 内置设置参考手册 BOT_NAME 默认: scrapybot 当您使用 sta…...

Jmeter接口测试工具的一些使用小技巧

如何使用英文界面的JMeter Jmeter启动时会自动判断操作系统的locale 并选择合适的语言启动&#xff0c;所以&#xff0c;我们启动jmeter后&#xff0c;其会出现一个倍感亲切的中文界面。但由于jmeter本身的汉化工作做得不好&#xff0c;你会看到有未被汉化的选项及元件的参数。…...

黄金眼PAAS化数据服务DIFF测试工具的建设实践 | 京东云技术团队

一、背景介绍 黄金眼PAAS化数据服务是一系列实现相同指标服务协议的数据服务&#xff0c;各个服务间按照所生产指标的主题作划分&#xff0c;比如交易实时服务提供实时交易指标的查询&#xff0c;财务离线服务提供离线财务指标的查询。黄金眼PAAS化数据服务支撑了黄金眼APP、黄…...

深入了解RPA业务流程自动化的关键要素

在RPA业务流程自动化实施过程中&#xff0c;哪些因素起着至关重要的作用&#xff1f;这其实没有一个通用的答案&#xff0c;每一个RPA业务流程自动化的部署&#xff0c;都需要结合具体场景去调整&#xff0c;并且进行全面的规划。 首当其冲是要关注以下几点&#xff1a; 1、专…...

CSS记录

1.标准的CSS的盒子模型&#xff1f;与低版本IE的盒子模型有什么不同的&#xff1f; 标准盒子模型box-sizing: border-box; 宽度内容的宽度&#xff08;content&#xff09; border padding margin 低版本IE盒子模型&#xff1a;宽度内容宽度&#xff08;contentborderpaddin…...

Kotlin中类型转换

在 Kotlin 中&#xff0c;类型转换是一种常见的操作&#xff0c;用于将一个数据类型转换为另一个数据类型。在本篇博客中&#xff0c;我们将介绍 Kotlin 中的类型转换&#xff0c;并提供示例代码演示智能类型转换、强制类型转换以及可空类型的转换。 智能类型转换是 Kotlin 中…...

P7557 [USACO21OPEN] Acowdemia S

典型二分&#xff1a; #include<bits/stdc.h> using namespace std; #define int long long const int N1e510; int n,a[N],k,l; bool check(int x) {int cnt0,ans0;for(int i1; i<x; i) {if(a[i]>x) {cnt;continue;}else{if(x-a[i]>k)return false;else{ansans…...