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也会保证某个变量的可见性

 

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Keil实现Flash升级跳转(STM32/GD32/HC32)

编写BOOT程序&#xff0c;和APP程序。 BOOT程序检查OTA参数&#xff0c;执行OTA升级&#xff0c;然后跳转到APP代码。 记录一下跳转APP需要修改得东西&#xff1a; 1、BOOT程序 修改跳转地址 先检查APP地址是否有效 然后关闭外设 反初始化 设置MSP指针&#xff0c;进行跳转 …

工控机通过Profinet转Modbus RTU网关连接变频器与电机通讯案例

在工业自动化系统中&#xff0c;工控机扮演着重要的角色&#xff0c;它是数据采集、处理和控制的中心。工控机通过Profinet转Modbus RTU网关连接变频器与电机通讯&#xff0c;为工业自动化系统中的设备之间的通信提供了解决方案。工控机通过Profinet转Modbus RTU网关的方式&…

C语言进行实验:通过程序实现线算图取值【支持VC++ 6.0编辑器环境运行】

背景&#xff1a; 一、实验目的和要求 1、能描述数据基本类型及其常量的表示方法&#xff1b; 2、会对变量进行定义及初始化&#xff1b; 3、能使用运算符与表达式对变量赋值&#xff1b; 4、会描述C语句的概念及种类、C语言常用的输入/出方式&#xff1b; 5、会设计顺序…

python+pytest接口自动化之测试函数、测试类/测试方法的封装

前言 今天呢&#xff0c;笔者想和大家聊聊pythonpytest接口自动化中将代码进行封装&#xff0c;只有将测试代码进行封装&#xff0c;才能被测试框架识别执行。 例如单个接口的请求代码如下&#xff1a; import requestsheaders {"user-agent": "Mozilla/5.0…

【微信小程序】自定义组件布局会议OA其他页面(附源码)

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《微信小程序开发实战》。&#x1f3af;&#x1f3a…

JOSEF约瑟 JD3-40/23 JD3-70/23漏电继电器 AC220V\0.05-0.5A

JD3系列漏电继电器&#xff08;以下简称继电器&#xff09;适用于交流电压至1140V&#xff0c;频率为50Hz&#xff0c;该继电器与分励脱扣器或失压脱扣器的断路器、交流接触器、磁力启动器等组成漏电保护装置&#xff0c;作漏电和触电保护之用&#xff0c;可配备蜂鸣器、信号等…

【会议征稿通知】第三届大数据经济与数字化管理国际学术会议(BDEDM 2024)

2024 3rd International Conference on Big Data Economy and Digital Management 第三届大数据经济与数字化管理国际学术会议&#xff08;BDEDM 2024&#xff09; 第三届大数据经济与数字化管理国际学术会议&#xff08;BDEDM 2024&#xff09;将于2024年1月12-14日于宁波召…

性能测试-redis常见问题

缓存击穿、缓存穿透、缓存雪崩 缓存雪崩 解决办法 1.设置缓存失效时间&#xff0c;不要在同一时间 2.redis集群部署 3.不设置缓存设置时间 4.定时刷缓存的时间 缓存穿透 请求不管返回什么数据都返回给redis对参数合法器进行验证&#xff0c;不合法的时候直接过滤掉使用布…

周四见|物流人的一周资讯

中国生鲜快消品电商渗透率居全球首位 10月19日消息&#xff0c;中国连锁经营协会与贝恩公司近日联合发布《2023中国生鲜快消品零售业态发展趋势研究》&#xff0c;报告指出&#xff0c;当前&#xff0c;中国生鲜快消品零售市场正处于电商渗透率引领全球&#xff0c;但集中度较…

python使用dataset快速使用SQLite

目录 一、官网地址 二、安装 三、 快速使用 一、官网地址 GitHub - pudo/dataset: Easy-to-use data handling for SQL data stores with support for implicit table creation, bulk loading, and transactions. 二、安装 pip install dataset 如果是mysql&#xff0c;则…

基于springboot实现线上教学平台项目【项目源码+论文说明】计算机毕业设计

摘要 在社会快速发展的影响下&#xff0c;使线上教学平台的管理和运营比过去十年更加理性化。依照这一现实为基础&#xff0c;设计一个快捷而又方便的网上线上教学平台系统是一项十分重要并且有价值的事情。对于传统的线上教学平台控制模型来说&#xff0c;网上线上教学平台系…

VR全景广告:让消费者体验沉浸式交互,让营销更有趣

好的产品都是需要广告宣传的&#xff0c;随着科技的不断发展&#xff0c;市面上的广告也和多年前的传统广告不同&#xff0c;通过VR技术&#xff0c;可以让广告的观赏性以及科技感更加强烈&#xff0c;并且相比于视频广告&#xff0c;成本也更低。 在广告营销中&#xff0c;关键…

【MySQL】索引的增删查

上篇博客讲解了索引的底层结构 本篇介绍索引的使用 文章目录 一. 主键索引二. 唯一键索引三. 普通索引四. 全文索引五. 查询索引六. 删除索引结束语 一. 主键索引 MySQL默认会按照主键索引进行排序 关键字&#xff1a;primary key 即使建表时没有指明主键&#xff0c;MySQL也会…

Stm32_标准库_16_串口蓝牙模块_手机与蓝牙模块通信_手机传入信息能对芯片时间日期进行更改

实现了手机发送信息给蓝牙模块&#xff0c;程序对数据进行分析拆解&#xff0c;并更新自身数据 main.c: #include "stm32f10x.h" // Device header #include "Delay.h" #include "OLED.h" #include "Serial.h" #include "Ti…

Linux进阶-ipc共享内存

目录 共享内存 shmget()&#xff1a;创建或获取共享内存 shmat()&#xff1a;映射 shmdt()&#xff1a;解除映射 shmctl()&#xff1a;获取或设置属性 sem.h文件 sem.c文件 shm.c文件 Makefile文件 执行过程 共享内存 共享内存&#xff1a;将内存进行共享&#xff0c…

利用向导创建MFC

目录 1、项目的创建&#xff1a; 2、项目的管理 &#xff1a; 3、分析以及生成的项目代码 &#xff1a; &#xff08;1&#xff09;、查看CFrame中的消息映射宏 &#xff08;2&#xff09;、自动生成事件 &#xff08;3&#xff09;、在CFrame中添加对应的鼠标处理函数 …

计算机网络学习笔记(三):数据链路层(待更新)

目录 3.1 基本概念 3.1.1 数据链路和帧 3.1.2 三个基本问题 3.2 类型1&#xff1a;使用点对点信道的数据链路层&#xff08;路由器&#xff09; 3.2.1 点对点协议 PPP&#xff1a;特点 3.2.2 点对点协议 PPP&#xff1a;帧格式 3.2.3 点对点协议 PPP&#xff1a;工作状态 …

华为---PPP协议简介及示例配置

PPP协议简介 PPP是Point-to-Point Protocol的简称&#xff0c;中文翻译为点到点协议。与以太网协议一样,PPP也是一个数据链路层协议。以太网协议定义了以太帧的格式&#xff0c;PPP协议也定义了自己的帧格式&#xff0c;这种格式的帧称为PPP帧。 利用PPP协议建立的二层网络称为…

CSS盒子模型的详细解析

03-盒子模型 作用&#xff1a;布局网页&#xff0c;摆放盒子和内容。 盒子模型-组成 内容区域 – width & height 内边距 – padding&#xff08;出现在内容与盒子边缘之间&#xff09; 边框线 – border 外边距 – margin&#xff08;出现在盒子外面&#xff09; d…

基于Spring boot轻松实现一个多数据源框架

Spring Boot 提供了 Data JPA 的包&#xff0c;允许你使用类似 ORM 的接口连接到 RDMS。它很容易使用和实现&#xff0c;只需要在 pom.xml 中添加一个条目&#xff08;如果使用的是 Maven&#xff0c;Gradle 则是在 build.gradle 文件中&#xff09;。 <dependencies>&l…