Java并发编程(一)多线程基础概念
概述
-
多线程技术:基于软件或者硬件实现多个线程并发执行的技术
-
线程可以理解为轻量级进程,切换开销远远小于进程
-
在多核CPU的计算机下,使用多线程可以更好的利用计算机资源从而提高计算机利用率和效率来应对现如今的高并发网络环境
-
- 并发编程核心三要素
- 原子性
- 原子,即一个不可再被分割的颗粒。在Java中原子性指的是一个或多个操作要么全部执行成功要么全部执行失败
- 有序性
- 程序执行的顺序按照代码的先后顺序执行。(处理器可能会对指令进行重排序)
- 可见性
- 当多个线程访问内存中同一个变量时,如果其中一个线程对其作了修改,其他线程能立即获取到最新的值
- 原子性
- 并行 VS 并发
- 并行:在同一时刻,有多个指令在多个CPU上同时执行

- 并发:在同一时刻,有多个指令在单个CPU上交替执行

- 并行:在同一时刻,有多个指令在多个CPU上同时执行
- 同步 VS 异步 && 阻塞 VS 非阻塞
同步和异步指的对于消息结果的获取是客户端(调用者)主动获取[同步],还是由服务端(提供者)主动通知客户端[异步];阻塞和非阻塞指的一个是客户端(调用者)等待消息处理时的本身状态是挂起[阻塞]还是继续处理其他事[非阻塞];
- 同步阻塞:客户端主动发起获取结果,而同时一直在等待应答结果
- 同步非阻塞:客户端主动发起获取结果,而同时不断轮询查看发起的请求是否有应答结果
- 异步阻塞:客户端发出请求后,服务端会主动通知,而客户端同时一直在等待通知
- 异步非阻塞:客户端发出请求后,服务端会主动通知,而客户端在获取服务端通知之前已经去处理其他事情
- 进程 VS 线程
- 进程:是系统运行的基本单位
- 独立性: 进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位
- 动态性: 进程的实质是程序的一次执行过程,进程是动态产生,动态消亡的
- 并发性: 任何进程都可以同其他进程一起并发执行
- 线程:是进程中的单个顺序控制流,是一条执行路径
- 单线程: 一个进程如果只有一条执行路径,则称为单线程程序
- 多线程: 一个进程如果有多条执行路径,则称为多线程程序
- 进程:是系统运行的基本单位
- 悲观锁 VS 乐观锁
- 悲观锁: 每次操作都会加锁,会造成线程阻塞
- 乐观锁: 每次操作不加锁而是假设没有冲突而去完成某项操作,若因为冲突导致失败就重试,直到成功为止,不会造成阻塞
-
线程安全
线程安全问题指的是内存的安全,在每个进程的内存空间中都会有一块共享区域称为堆内存,进程内的所有线程都可以访问到该区域,这就是造成线程安全问题的潜在原因。所以在堆内存中的数据由于可以被任何线程访问到,在没有限制的情况下存储在内存的数据(主要是全局变量和静态变量)可能被别的线程修改。因此在多线程场景下,我们需要通过相关限制实现线程安全
-
上下文切换:线程在执行过程中都会有自己的运行条件和状态,这些运行条件和状态我们就称之为线程上下文,这些信息例如程序计数器、虚拟机栈、本地方法栈等信息。当出现以下几种情况时线程就会从占用CPU状态中退出:
- 线程主动让出CPU,例如调用wait或者sleep等方法
- 线程的CPU 时间片用完 而退出CPU占用状态 (因为操作系统为了避免某些线程独占CPU导致其他线程饥饿的情况就设定的例如时间分片算法)
- 线程调用了阻塞类型的系统中断,例如IO请求等
- 线程被终止或者结束运行
- 上述的前三种情况都会发生上下文切换为了保证线程被切换在恢复时能够继续执行,所以上下文切换都需要保存线程当前执行的信息,并恢复下一个要执行线程的现场。所以会占用CPU和内存资源,频繁的进行上下文切换就会导致整体效率低下
- 死锁:指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。死锁的必要条件包括
- 互斥条件:进程要求对所分配的资源进行排它性控制,即在一段时间内某资源仅为一进程所占用
- 请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放
- 不剥夺条件:进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放
- 环路等待条件:在发生死锁时,必然存在一个进程--资源的环形链
线程分类
- 普通线程:即前台线程或者用户线程;即应用程序运行的线程
- 守护线程:即服务线程或者后台线程;优先级较低,作用是为其他线程的运行提供便利服务
- JRE判断程序是否执行结束的标准是所有的前台执行线程执行完毕,而不管后台线程(守护线程)的状态
- 守护线程最典型的应用就是GC
- 除虚拟机提供的如GC守护线程外,开发者也可以设置线程为守护线程
线程生命周期(线程状态)

根据sun官网可知,线程生命周期在java中有以下几种状态:初始(NEW) ,可运行(RUNNABLE),运行(Running),阻塞(BLOCKED)和终止(TERMINATED)
- 初始(NEW): 如果创建Thread类的实例但在调用start()方法之前,线程处于初始(NEW)状态。
- 可运行(RUNNABLE): 调用start()方法后,线程处于runnable状态,但线程调度程序尚未选择它作为正在运行的线程
- 运行(RUNNING): 如果线程调度程序已选择它,则线程处于运行状态
- 阻塞(BLOCKED): 此时线程仍处于活动状态但当前没有资格运行的状态
- 终止(TERMINATED): 当run()方法退出时,线程处于终止或死亡状态
线程间通信方式
很多业务场景下某个任务都是通过多个线程一起完成,此时就需要线程之间进行通信一起完成该任务,线程通信就是当多个线程共同操作共享的资源时,互相告知自己的状态以避免资源争夺;线程通信主要可以分为三种方式,分别为共享内存、消息传递和管道流。
- 共享内存:线程之间共享程序的公共状态,线程之间通过读-写内存中的公共状态来隐式通信
- volatile关键字,该方式是最简单的一种实现方式,volatile修饰的变量是共享内存,保证内存可见性,让多个线程共享一个标志位,当标志位更改的时候就执行不同的线程
- 消息传递:线程之间没有公共的状态,线程之间必须通过明确的发送信息来显示的进行通信
- Object类的 wait/notify方法,执行wait()方法,该线程释放锁资源,然后执行notify() 方法,唤醒其他的线程
- condition类的 await/signal方法,condition通过lock对象创建,await操作会立刻释放掉锁,进入阻塞状态,signal会唤醒等待队列中的头节点(失败就依次唤醒)
- 管道流: 管道的输入和输出实际上使用的是一个循环缓冲数组来实现的
创建线程
- 通过继承Thread,重写其中的run方法,通过调用start方法启动线程时
- 通过实现Runnable接口,重写其中的run方法,并将自定义类的实例作为一个参数传入Thread获取线程实例,再调用start方法启动线程
- 通过实现callable接口的,重写其中的call方法。新建Callable的实例,将Callable传入FutureTask得到FutureTask实例,再将FutureTask实例传入Thread获得Thread实例,调用Thread的start方法启动线程,并通过FutureTask的get方法来获取返回值
- 通过线程池提前为我们准备好线程,我们需要的时候直接取用就可以了,而不需要自己创建
线程Thread常用API
- start():在使用 new 关键字创建一个线程后(New 状态),只有在 start() 方法执行后,才表示这个线程可运行了(Runnable 状态),至于何时真正运行还要看线程调度器的调度;在线程死亡后,不要再次调用 start() 方法。只能对新建状态的线程调用且只能调用一次 start() 方法,否则将抛出 IllegalThreadStateException 异常
- run():启动线程是 start() 方法,而不是 run() 方法。如果直接调用 run() 方法,这个线程中的代码会被立即执行,多个线程就无法并发执行了
- join():等待该线程完成的方法,其他线程将进入等待状态(Waiting 状态),通常由使用线程的程序(线程)调用,如将一个大问题分割为许多小问题,要等待所有的小问题处理后,再进行下一步操作
- sleep():主动放弃占用的处理器资源,该线程进入阻塞状态(Blocked 状态),指定的睡眠时间超时后,线程进入就绪状态(Runnable),等待线程调度器的调用。
- yield():主动放弃占用的处理器资源,线程直接进入就绪状态(Runnable),等待线程调度器的调用。可能出现当线程使用yield方法放弃执行后,线程调度器又将该线程调度执行
- interrupt():没有任何强制线程终止的方法,这个方法只是请求线程终止,这个方法并没有实际的用途,还有 isInterrupted() 方法检查线程是否被中断
- setDaemon():设置守护进程,该方法必须在 start() 方法之前调用,判断一个线程是不是守护线程,可以使用 isDaemon() 方法判断
- setPriority():设置线程的优先级,理论上讲线程优先级高更容易被执行。每个线程默认的优先级和父线程(如 main 线程、普通优先级)的优先级相同,线程优先级区间为 1~10,三个静态变量:MIN_PRIORITY = 1、NORM_PRIORITY = 5、MAX_PRIORITY = 10。使用 getPriority() 方法可以查看线程的优先级
- isAlive():检查线程是否处于活动状态,如果线程处于就绪、运行、阻塞状态,方法返回 true,如果线程处于新建和死亡状态,方法返回 false
线程常见问题
sleep VS wait
- sleep不会释放锁,只是单纯休眠一会,在给定时间后就会苏醒;而wait则会释放锁,若wait没有设定时间,只能通过notify或者notifyAll唤醒
- wait常用于线程之间的通信或者交互,而sleep单纯让线程让出执行权。
- sleep是Thread的方法,因为sleep要做的仅仅是让线程休眠,所以不涉及任何锁释放等逻辑,放在Thread上最合适;而wait是Object的方法,调用wait时会释放锁,并让对象进入WAITING状态,会涉及到资源释放等问题,所以我们需要将wait放在Object 类上
run VS start
- run: 仅仅是方法,若直接通过对象调用run方法,那么该方法和普通方法没有任何差别,它仅仅是一个名字为run的普通方法
- start: 开启线程方法,线程就会从用户态转内核态创建线程,并在获取CPU时间片的时候开始运行,然后运行run方法
Thread,Runnable,Callable区别
- Thread 和 Runnable 区别
- Thread 是类,而Runnable是接口;如果有其他类需要继承的话肯定是用Runnable接口了
- Thread本身是实现了Runnable接口的类。我们知道“一个类只能有一个父类,但是却能实现多个接口”,因此Runnable具有更好的扩展性
- Callable和Runnable区别
- 相同点
- 都是接口实现,具有良好的扩展性
- 不同点
- Callable可以在任务结束的时候提供一个返回值,Runnable无法提供这个功能
- Callable的call方法可以抛出异常,而Runnable的run方法不能抛出异常
- 相同点
相关文章:
Java并发编程(一)多线程基础概念
概述 多线程技术:基于软件或者硬件实现多个线程并发执行的技术 线程可以理解为轻量级进程,切换开销远远小于进程 在多核CPU的计算机下,使用多线程可以更好的利用计算机资源从而提高计算机利用率和效率来应对现如今的高并发网络环境 并发编程…...
D. Strong Vertices - 思维 + 二分
分析: 首先找到边的指向很容易,但是暴力是o(n2),超时,可以将给定的式子变形,au - av > bu - bv即au - bu > av - bv,可以将两个数组转变为一个数组中的任意两个值之间的关系,因…...
8月9日上课内容 nginx负载均衡
负载均衡工作当中用的很多的,也是面试会问的很重要的一个点 负载均衡:通过反向代理来实现(nginx只有反向代理才能做负载均衡) 正向代理的配置方法(用的较少) 反向代理的方式:四层代理与七层代…...
为何我们都应关心算法备案?
随着技术的日新月异,算法成为现代生活的核心组成部分,从社交媒体推荐、在线广告到智能交通管理,几乎无处不在。然而,如此普及的技术给我们带来了一个新的挑战:如何确保算法的透明度、公正性和道德性?答案可…...
[IDEA]使用idea比较两个jar包的差异
除了一些小工具外,idea自带了jar包比较的功能。 把需要比对的jar包放到任意目录下,然后选中两个需要比较的jar包,右键,选择Compare Archives,然后就可以比较了。 这次疏忽了,每次打包前需要commit界面看一下…...
HTML笔记(2)
列表标签 项目标识符(项目符号)一般是不需要的 代码演示 改变符号样式,type属性 表格标签 代码演示 练习案例 布局标签 div是块儿级标签,占一整行; span标签不会占一整行,它只占包裹内容的那块儿区域&a…...
前端大屏自适应缩放
简介 前端中大屏往往用于展示各种炫酷的界面和特效,因此特别受用好欢迎。 但是在开发过程中,常常也会出现各种问题,与一般的页面相比, 最让人头疼的是大屏的自适应问题。使用CSS中transform属性和js获取缩放比例方法 先简单写一下…...
【Express.js】全面鉴权
全面鉴权 这一节我们来介绍一下 Passport.js,这是一个强大的 NodeJS 的认证中间件 Passport.js 提供了多种认证方式,账号密码、OpenID、ApiKey、JWT、OAuth、三方登录等等。 使用 Passport.js 认证要配置三个部分: 认证策略中间件会话 接…...
了解华为(H3C)网络设备和OSI模型基本概念
目录 一,认识华为 1.华为发展史 2.华为网络设备介绍 3.VRP概述 二,OSI七层模型 1.七层模型详细表格 2.各层的作用 3.数据在各层之间的传递过程 4.OSI四层网络模型 一,认识华为 官网:https://www.huawei.com/cn/ 1.华为发…...
Web3到底是个啥?
Web3是近两年来科技领域最火热的概念之一,但是目前对于Web3的定义却仍然没有形成标准答案,相当多对于Web3的理解,都是建立在虚拟货币行业(即俗称的“币圈”)的逻辑基础之上的。 区块链服务网络(BSN&#x…...
山东高校的专利申请人经常掉进的误区2
02、专利技术交底书只提供简单思路 一些高校科研人员在申请专利时,给专利代理人的技术交底书往往只给出了思路,或者技术方案不够详细,或者根本不会有实验验证过程和数据。 事实上,专利技术交底书的详尽程度将直接影响代理人对技…...
关于webpack的基本配置
文章目录 前言一、webpack基本配置1.配置拆分和merge2. 启动服务3、处理es6,配置babel4、处理样式5、处理图片 前言 为什么要有webpack构建和打包? 更好的模块化管理。webpack支持模块化规范:代码分割成独立模块,并管理模块之间…...
SpringBoot WebSocket配合react 使用消息通信
引入websocket依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency>配置websocket import org.springframework.context.annotation.Bean; import org.spr…...
【积水成渊】uniapp高级玩法分享
大家好,我是csdn的博主:lqj_本人 这是我的个人博客主页: lqj_本人_python人工智能视觉(opencv)从入门到实战,前端,微信小程序-CSDN博客 最新的uniapp毕业设计专栏也放在下方了: https://blog.csdn.net/lbcy…...
在指定的 DSN 中,驱动程序和应用程序之间的体系结构不匹配
1.Cadence 17.2 配置CIS数据库报:ERROR(ORCIS-6245): Database Operation Failed 安装cadance17.2以上版本时,ERROR(ORCIS-6245): Database Operation Failed_收湾湾的博客-CSDN博客 原因是ODBC数据库没有配置,或者没有驱动, 驱…...
API接口 |产品经理一定要懂的技术知识
什么是接口❓ 要理解接口是什么,首先理解一下为什么要用接口? 两个独立的系统,它们的数据或程序是独立的,这就使得它们无法直接访问对方的数据库或程序(两个独立的数据相当于两个独立的家庭,每个家庭肯定是…...
C++中访问存储在数组中的数据
C中访问存储在数组中的数据 要访问数组中的元素,可使用从零开始的索引。这些索引之所以被称为从零开始的,是因为数组中第一个元素的索引为零。因此,存储在数组 myNumbers 中的第一个整数值为 myNumbers[0],第二个为 myNumbers[1]…...
【创建型设计模式】C#设计模式之原型模式
原型模式是一种创建型设计模式,它通过复制现有对象来创建新对象,而无需通过实例化的方式。它允许我们使用已经存在的对象作为蓝本,从而创建新的对象,这样可以避免重复初始化相似的对象,提高了对象的创建效率。 现在给…...
用C语言高效地打印杨辉三角
假设杨辉三角的通项公式为a(n),则打印形式如下: 然而我们知道,它应该是这样的: 三角形两边的值都为1,且每个元素的值都为该元素正上方和其正上方前面的元素的值之和。 为了实现这个代码,我们需要知道每行首…...
TCP/IP四层模型对比OSI七层网络模型的区别是啥?数据传输过程原来是这样的
一、TCP/IP四层模型对比OSI七层模型 它们两个定义的一些功能和协议都是差不多的。TCP/IP四层协议模型比我们的七层少了三层,把我们的数据链路层和物理层放在一层里面了,叫做数据链路层(网络接口层),对应网络协议也没有…...
Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...
HTML 列表、表格、表单
1 列表标签 作用:布局内容排列整齐的区域 列表分类:无序列表、有序列表、定义列表。 例如: 1.1 无序列表 标签:ul 嵌套 li,ul是无序列表,li是列表条目。 注意事项: ul 标签里面只能包裹 li…...
【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度
文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...
【VLNs篇】07:NavRL—在动态环境中学习安全飞行
项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战,克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...
MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用
文章目录 一、背景知识:什么是 B-Tree 和 BTree? B-Tree(平衡多路查找树) BTree(B-Tree 的变种) 二、结构对比:一张图看懂 三、为什么 MySQL InnoDB 选择 BTree? 1. 范围查询更快 2…...
苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会
在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...
日常一水C
多态 言简意赅:就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过,当子类和父类的函数名相同时,会隐藏父类的同名函数转而调用子类的同名函数,如果要调用父类的同名函数,那么就需要对父类进行引用&#…...
数据结构:递归的种类(Types of Recursion)
目录 尾递归(Tail Recursion) 什么是 Loop(循环)? 复杂度分析 头递归(Head Recursion) 树形递归(Tree Recursion) 线性递归(Linear Recursion)…...
保姆级【快数学会Android端“动画“】+ 实现补间动画和逐帧动画!!!
目录 补间动画 1.创建资源文件夹 2.设置文件夹类型 3.创建.xml文件 4.样式设计 5.动画设置 6.动画的实现 内容拓展 7.在原基础上继续添加.xml文件 8.xml代码编写 (1)rotate_anim (2)scale_anim (3)translate_anim 9.MainActivity.java代码汇总 10.效果展示 逐帧…...
