Java并发基础知识

2023-11-04

基础概念

什么是进程和线程
进程是程序运行资源分配的最小单位,是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。
线程是 CPU 调度的最小单位,必须依赖于进程而存在,与同属一个进程的其 他的线程共享进程所拥有的全部资源。

CPU核心数和线程数的关系
目前主流 CPU 都是多核的。增加核心数目就是为了增加线程数,因为操作系统是通过线程来执行任务的,一般情况下它们是 1:1 对应关系,也 就是说四核 CPU 一般拥有四个线程。但 Intel 引入超线程技术后,使核心数与线程数形成 1:2 的关系。

CPU时间片轮转机制
如果在时间片结束时进程还在运行,则 CPU 将被剥夺并分配给另一个进程。 如果进程在时间片结束前阻塞,则 CPU 当即进行切换。

并行和并发
并发:指应用能够交替执行不同的任务,比如单 CPU 核心下执行多线程并非是 同时执行多个任务,如果你开两个线程执行,就是在你几乎不可能察觉到的速度不 断去切换这两个任务,已达到"同时执行效果",其实并不是的,只是计算机的速度太快,我们无法察觉到而已。

并行:指应用能够同时执行不同的任务,例:吃饭的时候可以边吃饭边打电话, 这两件事情可以同时执行。

区别:前者是交替执行,一个是同时执行。

高并发编程的意义、好处和注意事项

  • 充分利用 CPU 的资源
  • 加快响应用户的时间
  • 可以使你的代码模块化,异步化,简单化

注意事项

  • 线程之间的安全性
  • 线程之间的死锁
  • 线程太多了会将服务器资源耗尽形成死机宕机

认识Java里的线程

Java 程序天生就是多线程的
一个 Java 程序从 main()方法开始执行,就会开启以下线程:

  • main线程,用户程序入口
  • Reference Handle,清除 Reference 的线程
  • Finalizer,调用对象finalize方法的线程
  • Signal Dispatcher,分发处理发送给 JVM 信号的线程
  • Attach Listener,内存 dump,线程 dump,类信息统计,获取系统属性等
  • Monitor Ctrl-Break,监控 Ctrl-Break 中断信号

线程的启动与中止
启动方式:

  1. 继承Thread类,然后调用start;
  2. 实现Runnable接口,然后交给Thread运行;

Thread 和 Runnable 的区别:Thread才是Java里对线程的唯一抽象,Runnable只是对任务(业务逻辑)的抽象。Thread可以接受任意一个Runnable的实例并执行.

中止:

  • 线程自然中止,要么是run执行完成了,要么是抛出了一个未处理的异常导致线程提前结束。
  • stop,暂停、恢复和停止操作对应在线程Thread的API就是suspend()、resume()和stop(),但是这些API是过期的,也就是不建议使用的。,以stop方法为例,在终结一个线程时不会保证线程的资源正常释放,通常是没有给予线程完成资源释放工作的机会,因此会导致程序可能工作在不确定状态下。

中断:安全的中止则是其他线程通过调用某个线程A的interrupt()方法对其进行中断操作,中断好比其他线程对该线程打了个招呼,“A,你要中断了”,不代表线程A会立即停止自己的工作,同样的A线程完全可以不理会这种中断请求。因为java里的线程是协作式的,不是抢占式的。线程通过检查自身的中断标志位是否被置为true来进行响应。

线程通过方法 isInterrupted()来进行判断是否被中断,也可以调用静态方法 Thread.interrupted()来进行判断当前线程是否被中断,不过 Thread.interrupted() 会同时将中断标识位改写为 false

如果一个线程处于了阻塞状态(如线程调用了 thread.sleep、thread.join、 thread.wait 等),则在线程在检查中断标示时如果发现中断标识为 true,则会在 这些阻塞方法调用处抛出 InterruptedException 异常

不建议自定义一个取消标志位来中止线程的运行。因为 run 方法里有阻塞调用时会无法很快检测到取消标志,线程必须从阻塞调用返回后,才会检查这个取消标志。这种情况下,使用中断会更好,因为:
1.一般的阻塞方法,如 sleep 等本身就支持中断的检查。
2.检查中断位的状态和检查取消标志位没什么区别,用中断位的状态还可 以避免声明取消标志位,减少资源的消耗。【处于死锁状态的线程无法被中断

深入理解run()和start()

Thread类是Java里对线程概念的抽象,可以这样理解:我们通过new Thread() 其实只是 new 出一个 Thread 的实例,还没有操作系统中真正的线程挂起钩来。 只有执行了 start()方法后,才实现了真正意义上的启动线程。 start()方法让一个线程进入就绪队列等待分配 cpu,分到 cpu 后才调用实现 的 run()方法,start()方法不能重复调用,如果重复调用会抛出异常。 而 run 方法是业务逻辑实现的地方,本质上和任意一个类的任意一个成员方 法并没有任何区别,可以重复执行,也可以被单独调用。

其他的线程相关方法
yield方法:使当前线程让出 CPU 占有权,但让出的时间是不可设定的。也不会释放锁资源。注意:并不是每个线程都需要这个锁的,而且执行 yield( )的线程不一定就会持有锁,我们完全可以在释放锁后再调用 yield 方法。 所有执行 yield()的线程有可能在进入到就绪状态后会被操作系统再次选中 马上又被执行。

join方法:把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行。 比如在线程 B 中调用了线程 A 的 Join()方法,直到线程 A 执行完毕后,才会继续执行线程 B。

线程的优先级
在 Java 线程中,通过一个整型成员变量 priority 来控制优先级,优先级的范围从 1~10,在线程构建的时候可以通过 setPriority(int)方法来修改优先级,默认优先级是 5,优先级高的线程分配时间片的数量要多于优先级低的线程。
设置线程优先级时,针对频繁阻塞(休眠或者 I/O 操作)的线程需要设置较高优先级,而偏重计算(需要较多 CPU 时间或者偏运算)的线程则设置较低的优先级,确保处理器不会被独占。在不同的 JVM 以及操作系统上,线程规划会存在差异,有些操作系统甚至会忽略对线程优先级的设定。

守护线程
Daemon(守护)线程是一种支持型线程,因为它主要被用作程序中后台调度以及支持性工作。这意味着,当一个 Java 虚拟机中不存在非 Daemon 线程的时候,Java 虚拟机将会退出。可以通过调用 Thread.setDaemon(true)将线程设置为Daemon 线程。我们一般用不上,比如垃圾回收线程就是 Daemon 线程。

Daemon 线程被用作完成支持性工作,但是在 Java 虚拟机退出时 Daemon 线程中的 finally 块并不一定会执行。在构建 Daemon 线程时,不能依靠 finally块中的内容来确保执行关闭或清理资源的逻辑。

线程间共享和协作

线程间共享

synchronized 内置锁
概述:Java 支持多个线程同时访问一个对象或者对象的成员变量,关键字 synchronized 可以修饰方法或者以同步块的形式来进行使用,它主要确保多个线程在同一个时刻,只能有一个线程处于方法或者同步块中,它保证了线程对变量 访问的可见性和排他性,又称为内置锁机制。

对象锁和类锁:
对象锁是用于对象实例方法,或者一个对象实例上的,类锁是用于类的静态 方法或者一个类的class 对象上的。我们知道,类的对象实例可以有很多个,但是每个类只有一个 class 对象,所以不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁。

但是有一点必须注意的是,其实类锁只是一个概念上的东西,并不是真实存在的,类锁其实锁的是每个类的对应的 class 对象。类锁和对象锁之间也是互不干扰的。

线程间协作

等待/通知机制
概述:是指一个线程 A 调用了对象 O 的 wait()方法进入等待状态,而另一个线程 B 调用了对象 O 的 notify()或者 notifyAll()方法,线程 A 收到通知后从对象 O 的 wait() 方法返回,进而执行后续操作。上述两个线程通过对象 O 来完成交互,而对象 上的 wait()和 notify/notifyAll()的关系就如同开关信号一样,用来完成等待方和通知方之间的交互工作。
notify():通知一个在对象上等待的线程,使其从 wait 方法返回,而返回的前提是该线程 获取到了对象的锁,没有获得锁的线程重新进入WAITING 状态。
notifyAll():通知所有等待在该对象上的线程;
wait():调用该方法的线程进入 WAITING 状态,只有等待另外线程的通知或被中断 才会返回.需要注意,调用 wait()方法后,会释放对象的锁;
wait(long):超时等待一段时间,这里的参数时间是毫秒,也就是等待长达n 毫秒,如果没有通知就超时返回;
wait (long,int):对于超时时间更细粒度的控制,可以达到纳秒;

尽可能用 notifyall(),谨慎使用 notify(),因为 notify()只会唤醒一个线程,我 们无法确保被唤醒的这个线程一定就是我们需要唤醒的线程

Q:调用 yield() 、sleep()、wait()、notify()等方法对锁有何影响?
A:yield() 、sleep()被调用后,都不会释放当前线程所持有的锁。 调用 wait()方法后,会释放当前线程持有的锁,而且当前被唤醒后,会重新去竞争锁,锁竞争到后才会执行wait 方法后面的代码。 调用 notify()系列方法后,对锁无影响,线程只有在 syn 同步代码执行完后才 会自然而然的释放锁,所以 notify()系列方法一般都是 syn 同步代码的最后一行。

线程的状态

线程的状态
Java中线程的状态分为6种:

  1. 初始化状态(NEW) :新建一个线程对象,但还没有调用start()方法;
  2. 运行(RUNNING):Java线程中将就绪(ready)和运行中(running)两种状态统称为“运行”。
    线程对象创建后,其他线程(比如main线程)调用了该对象的start方法,该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready),就绪状态的线程获取CPU时间片后变成运行中状态(running)
  3. 阻塞(BLOCKED):表示线程阻塞于锁。【仅对应于synchronized关键字】
  4. 等待(WAITING): 进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)
  5. 超时等待(TIME_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。
  6. 终止(TERMINATED):表示该线程已经执行完毕。

死锁

概念

指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞现象,若无外力作用,他们都将无法推进下去,此时称系统处于死锁状态或系统产生了死锁。

死锁产生的条件

死锁的发生必须具备以下四个必要条件:

  1. 互斥:指进程对所分配到的资源进行排他性使用,即在一段时间内某资源只由一个进程占用,如果此时还有其他进程请求资源,则请求者只能等待,直到占有资源的进程用完释放。
  2. 请求和保持:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程阻塞,但又对自己已获得的其他资源保持不放。
  3. 不剥夺:指进程已获取的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
  4. 环路等待:指在发生死锁时,必然存在一个进程—资源的环形链,即进程集合{P0,P1,P2…Pn}中的P0正在等待一个P1占用的资源,P1正在等待P2占用的资源,…,Pn正在等待已被P0占用的资源。

打破死锁的方式

理解了死锁的原因,尤其是产生死锁的四个必要条件,就可以最大可能地避免、预防和解除死锁。

只要打破四个必要条件之一就可以有效预防死锁的发生。

  1. 打破互斥条件:改造独占性资源为虚拟资源,大部分资源已无法改造。
  2. 打破不可抢占条件:当一进程占有一独占性资源后又申请一独占性资源而无法满足,则退出原占有的资源。
  3. 打破占有且申请条件:采用资源预先分配策略,即进程运行前申请全部资源,满足则运行,不然就等待,这样就不会占有且申请。
  4. 打破循环等待条件:实现资源有序分配策略,对所有设备实现分类编号,所有进程只能采用按序号递增的形式申请资源。

避免死锁常见的算法有有序资源分配法、银行家算法。

死锁的危害

  1. 线程不工作了,但是整个程序还是活着的;
  2. 没有任何的异常信息可以供我们检查;
  3. 一旦程序发生了发生了死锁,是没有任何的办法恢复的,只能重启程序;

活锁

概念:两个线程在尝试拿锁的机制中,发生多个线程之间互相谦让,不断发生同一个线程总是拿到同一把锁,在尝试拿另一把锁时因为拿不到,而将本来已经持有的锁释放的过程。

活锁解决办法:每个线程休眠随机数,错开拿锁的时间。

线程饥饿

概念:低优先级的线程,总是拿不到执行时间。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java并发基础知识 的相关文章

随机推荐

  • 电巢独家直播|第七届世界无人机大会圆满落幕!

    当前 全球化秩序正进入新的调整阶段 数字化技术与生产生活深度融合 将加速推动数字化经济的全面渗透 中国经济也正由高速增长向高质量发展转变 这为无人机行业创造了更多的战略机遇 根据深圳市无人机行业协会统计 2022年 全国1 2万多家无人机企
  • 腾讯云服务器+ContOS 7.6安装MySql(二)

    腾讯云服务器 ContOS 7 6安装MySql 二 安装 使用XShell的登录到我们的云服务器 网上说在CentOS中默认安装有MariaDB 先来查查看有没有 root VM 4 7 centos yum search mysql L
  • 开源CmBacktrace移植到RT-Thread Nano

    一 CmBacktrace特性 CmBacktrace 是一款针对 ARM Cortex M 系列 MCU 的错误代码自动追踪 定位 错误原因自动分析的开源库 支持的错误包括 断言 assert 故障 Hard Fault Memory M
  • 解决vmware 本地连接时出错:地址仍未与网络终结点关联

    场景 使用vmware创建了server2008的虚拟机 使用的桥接模式 桥接模式是选择的自己的wifi网卡 虚拟机启动后发现无法联网 ipconfig查看 下图 发现ip地址段就不对 应该是192 168 1 尝试释放ip或者获取新的ip
  • python实现斐波拉契数列函数

    简单介绍一下 斐波拉契数列 斐波那契数列 Fibonacci sequence 又称黄金分割数列 因数学家莱昂纳多 斐波那契 Leonardoda Fibonacci 以兔子繁殖为例子而引入 故又称为 兔子数列 指的是这样一个数列 0 1
  • 企业架构LNMP学习笔记10

    1 Nginx版本 在实际的业务场景中 需要使用软件新版本的功能 特性 就需要对原有软件进行升级或重装系统 Nginx的版本需要升级迭代 那么如何进行升级呢 线上服务器如何升级 我们选择稳定版本 从nginx的1 14版本升级到nginx的
  • Git的简介和使用

    本文来自数据学习网 https www datalearner com 专注于机器学习方法 数据挖掘技术和编程技术 原文地址 https www datalearner com blog 1051521123408432 Git是一个版本控
  • IOException异常的处理方式

    首先看一段代码 这段代码有明显的IO异常 一般我们的做法是捕获异常 public static void main String args try 文件可能不存在 FileWriter fw new FileWriter W demo tx
  • 第5章 团队开发管理-测验题-作业

    1 在软件开发的各种资源中 D 是最重要的资源 A开发工具 B方法 C硬件环境 D人员 2 在攻克技术难题时 最佳的开发团队组织模型是 A A民主式结构 B主程序员式结构 C矩阵式结构 D以上所有选项都不是 3 在选择开发团队组织结构时应考
  • Java高级系列——异常(Exception)

    在解释Java中的异常时 首先我们来看一张图 上图是我们Java中 异常类的一个继承关系图 从图中我们可以看到Java标准库内构建的这些通用的异常 他们都是以Throwable为顶层父类 Throwable又派生出Error类和Except
  • 【idea】指定要排除拼写检查的单词

    问题 在使用idea的时候会自动检查我们的单词拼写是否错误 这是个方便又不方便的功能 因为我们有时候会使用一些特殊的命名 比如 AlipayConfig 那现在我们来修改一下设置 方法 1 鼠标悬停在该单词上 按 Alt enter组合键
  • mysql c3p0 参数_mysql Communications link failure,C3p0的参数详解

    MySQL默认一个连接空闲8小时候就会自动断开 而这时程序以为连接还能使用 然后在使用的时候就会出现Communications link failure异常 这时需要进行两步设置 有时候只设置MySQL就可以了 一 在MySQL的配置文件
  • Java开源的规则引擎 Drools 电商行业实战(含完整代码)

    前言 我所在项目组刚好接到一个领取优惠券需求 具体需求是用户领取的各种类型的优惠券 比如 代金券 折扣券 数量不能超过某个自定义数量 因考虑到领取限制数量是动态的 另外考虑到扩展性 满足将来业务规则的增长 不只是限制领取数需要新加其他条件
  • 2023年信号处理与机器学习国际研讨会(WSPML 2023)

    会议简介 Brief Introduction 2023年信号处理与机器学习国际研讨会 WSPML 2023 会议时间 2023年9月22 24日 召开地点 中国 杭州 大会官网 www wspml org 2023年信号处理与机器学习国际
  • 【目标检测适用】批量修改xml文件中的name字段

    前言 使用labelimg进行标注的时候 由于都是用的是默认的名称 有时候类的名字会出现拼写错误 比如我想要写的是 cow 结果打上去的是 cwo 一出错就错一片 这很常见 所以参考了 https www jianshu com p cf1
  • MySQL基础之DCL语句

    DCL Data Control Language 语句 数据控制语句 用途 控制数据库 表 字段 用户的访问权限和安全级别 常用关键字 grant revoke等 一般用于管理数据库和用户的权限 通过实用例子来学习grant 分配权限 和
  • vue禁止长按屏幕复制内容

    App vue 禁止长按屏幕复制内容 webkit touch callout none webkit user select none moz user select none ms user select none user selec
  • 【HTML】HTML面试知识梳理

    目录 DOCTYPE 文章类型 head标签 浏览器乱码的原因及解决 常用的meta标签与SEO script标签中defer和async的区别 src href区别 HTML5有哪些更新 语义化标签 媒体标签 表单 进度条 度量器 DOM
  • 刷脸支付降低实现数字商业的难度

    移动支付和银行卡支付没有办法确定使用者到底是谁 因为可以和家人等共同使用 刷脸支付可以确定消费实体是谁 定位到具体人确定数据标签 一位新零售从业者认为 刷脸支付除了提供更便捷的支付服务外 还可以提供更多的商业数据用于精细化经营 从垂直领域来
  • Java并发基础知识

    基础概念 什么是进程和线程 进程是程序运行资源分配的最小单位 是具有一定独立功能的程序关于某个数据集合上的一次运行活动 进程是系统进行资源分配和调度的一个独立单位 线程是 CPU 调度的最小单位 必须依赖于进程而存在 与同属一个进程的其 他