JavaEE初阶-多线程易忘点总结

文章目录

  • 1.PCB
      • PID
      • 文件描述符表
      • 内存指针
      • 状态
      • 上下文
      • 优先级
      • 记账信息
      • tgid
  • 2.线程与进程的区别
  • 3.sleep和interrupt方法的关系
      • 变量终止线程
      • interrupt方法终止线程
  • 4.线程状态
  • 5.出现线程不安全的原因
      • 线程在系统中是随即调度,抢占式执行的。
      • 多个线程修改同一个变量
      • 线程针对变量的修改操作不是“原子”的
      • 内存可见性
      • 指令重排序
  • 6.死锁发生的三种场景
      • 锁是不可重入锁,一个线程针对同一个锁对象连续加锁多次。
      • 两个线程两把锁。
      • N个线程,M把锁。
  • 7.死锁的必要条件(背)
      • 锁具有互斥性特性(基本特点)
      • 锁不可抢占(不可剥夺)(基本特点)
      • 请求和保持(代码结构)
      • 循环等待(代码结构)
  • 8.单例模式中的饿汉模式与懒汉模式的区别
      • 饿汉模式
      • 懒汉模式
  • 9.编译器优化
      • 内存可见性
      • 指令重排序
  • 10.阻塞队列-生产者消费者模型
      • 解耦合
      • 削峰填谷
  • 11.线程池
  • 12.定时器


1.PCB

PID

不同线程的PID是不同的。

文件描述符表

记录使用的文件资源。

内存指针

指向线程要使用数据以及指令。

状态

指明系统状态。

上下文

当线程切换出cpu停止执行,此时上下文会记录中间结果,方便切换回cpu后继续执行,这个过程和程序计数器有关。

优先级

给线程分配在cpu上执行的时间存在倾斜。

记账信息

操作系统也要避免一些线程一直吃不到资源,记录时间,给吃的少的多分配一点资源。

tgid

是进程的id,同一个进程下的不同线程是相同的。

2.线程与进程的区别

参考以下博客

3.sleep和interrupt方法的关系

变量终止线程

package Thread;public class Demo10 {private static boolean isRunning=true;public static void main(String[] args) {Thread t=new Thread(()->{while (isRunning) {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("线程已经终止");});t.start();try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("准备终止线程");isRunning=false;}
}

通过在主线程中修改变量的值,来跳出t线程中的循环。但是有一个缺点就是即使修改了变量循环可能也不会立刻结束,因为修改变量时可能线程t代码刚好执行到sleep,所以t不会立马终止,至少要等这一次循环执行完成后才能够终止。

interrupt方法终止线程

package Thread;public class Demo11 {public static void main(String[] args) {Thread t = new Thread(() -> {while (!Thread.currentThread().isInterrupted())System.out.println("hello Thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}});t.start();try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}t.interrupt();}
}

使用interrupt方法可以修改Thread.currentThread().isInterrupted()这个函数值为true,从而终止上述代码的线程t。如果说使用interrupt方法时线程t代码刚好执行到循环条件,那么t直接终止,如果使用interrupt方法时线程又执行到sleep,interrupt方法会直接唤醒线程t,但是同时会将Thread.currentThread().isInterrupted()这个函数值重新变为false,为了避免循环继续进行,此时就可以在sleep被唤醒的哪个trycatch中加入处理逻辑。

4.线程状态

在这里插入图片描述

5.出现线程不安全的原因

线程在系统中是随即调度,抢占式执行的。

多个线程修改同一个变量

线程针对变量的修改操作不是“原子”的

内存可见性

指令重排序

6.死锁发生的三种场景

锁是不可重入锁,一个线程针对同一个锁对象连续加锁多次。

两个线程两把锁。

N个线程,M把锁。

7.死锁的必要条件(背)

锁具有互斥性特性(基本特点)

一个线程拿到锁,如果另一个线程想要申请同一个锁就要阻塞等待。

锁不可抢占(不可剥夺)(基本特点)

一个线程拿到锁,除非自己释放,否则别人拿不走。

请求和保持(代码结构)

一个线程拿到一把锁之后,在不释放锁的前提下,去尝试获取其它锁。

循环等待(代码结构)

多个线程获取多个锁的过程中,出现了循环等待,A等待B,B又等待A。当代码中确实需要多个线程获取多把锁,约定好加锁的顺序,这样就能避免死锁。

8.单例模式中的饿汉模式与懒汉模式的区别

饿汉模式

package Thread;class Singleton {public static Singleton instance = new Singleton();public static Singleton getInstance() {return instance;}private Singleton() {}
}public class Demo31 {public static void main(String[] args) {Singleton s1 = Singleton.getInstance();Singleton s2 = Singleton.getInstance();System.out.println(s1 == s2);}
}

懒汉模式

package Thread;
class Singleton1 {public static Object locker = new Object();public static Singleton1 instance = null;public static Singleton1 getInstance() {if (instance == null) { //避免已经建立了对象重新上锁浪费性能,直接返回对象即可synchronized (locker) {if (instance == null) { //避免在多线程情况下重复创建对象,造成线程安全问题instance = new Singleton1();}}}return instance;}private Singleton1() {}
}public class Demo32 {public static void main(String[] args) {Singleton1 s1 = Singleton1.getInstance();Singleton1 s2 = Singleton1.getInstance();System.out.println(s1 == s2);}
}

饿汉模式在多线程的情况下使用getInstance方法是安全的,因为类对象已经创建好了,getInstance方法做的只是读。懒汉模式则是不安全的,因为在其getInstance方法中会创建类对象。通过给代码加锁会解决懒汉模式的线程安全问题,但是懒汉模式只有在创建类对象实例的时候会出现线程安全问题,创建以后也就是读。为了避免每次都要给代码加上一个锁给程序增加负担,在sychroinzed前面加上一个if语句进行判断,如果已经创建实例了就不用加锁了。

9.编译器优化

内存可见性

package Thread;import java.util.Scanner;public class Demo27 {private  static int count=0;public static void main(String[] args) {Thread t=new Thread(()->{while(count==0) {//}System.out.println("t1 执行结束");});Thread t2=new Thread(()->{Scanner scanner=new Scanner(System.in);System.out.println("输入数字:");count=scanner.nextInt();});t.start();t2.start();}}

下面这段代码会出现内存的可见性问题,将从内存中读取count值的操作称为load 判断操作称为cmp,load和cmp的执行速度差了好几个数量级,在线程2开始执行代码提示输入数字时,线程1的while循环已经执行了很多遍。java编译器会自动给代码进行优化,导致load只是第一次时真正从内存中读取count值,其余都是从cpu的寄存器中读取,然而线程2修改count是在内存中进行修改,线程1根本访问不到count的值,可以在变量前加上volatile关键字来提醒编译器不要优化。

指令重排序

指令重排序指的是编译器优化的一种,改变指令在cpu上执行的顺序,但是不影响最终的逻辑结果。对于单线程这样不会出现问题,但是多线程不行。

package Thread;//单例模式-懒汉模式
//在多线程的情况下是不安全的
class Singleton1 {public static Object locker = new Object();public static Singleton1 instance = null;public static Singleton1 getInstance() {if (instance == null) { //避免已经建立了对象重新上锁浪费性能,直接返回对象即可synchronized (locker) {if (instance == null) { //避免在多线程情况下重复创建对象,造成线程安全问题instance = new Singleton1();}}}return instance;}private Singleton1() {}
}public class Demo32 {public static void main(String[] args) {Singleton1 s1 = Singleton1.getInstance();Singleton1 s2 = Singleton1.getInstance();System.out.println(s1 == s2);}
}

举例说这里的懒汉模式的代码,在getInstance方法中建立对象分三步
(1)为对象申请空间。
(2)初始化空间(调用构造函数)。
(3)将地址赋给对象的引用。
现在假设一种情况,t1线程在执行上面的懒汉模式的getInstance方法时,因为编译器优化建立对象的指令顺序变为了1,3,2,那么如果t1线程运行到3时刚好t2线程运行到getInstance方法中的第一个判断语句,发现此时instance引用已经被赋值过了就直接返回,但是实际上这里的instance只是得到了地址,地址指向的空间并未初始化,这种情况就是指令重排序所造成的线程安全问题。
解决这种问题的方式也很简单,在你要处理的变量前面加上volatile即可告诉编译器这里不需要优化。

10.阻塞队列-生产者消费者模型

生产者消费者模型的两个优势:
(1)削峰填谷
(2)解耦合
一般在一个进程内的多线程中使用阻塞队列实现生产者消费者模型,在分布式系统中使用消息队列来实现。消息队列就是根据topic分为不同的阻塞队列,根据topic对不同的阻塞队列上进行操作。

解耦合

在这里插入图片描述
如果直接让服务器A和服务器B进行交互,那么它们必定会包含很多与彼此相关的代码。修改A会影响到B,修改B也会影响到A。
在这里插入图片描述
如上图引入一个消息队列,这样A只关心与队列的交互,B也只关心与队列的交互,因此A和B之间的互相影响就被减小非常多。

削峰填谷

在这里插入图片描述
对于服务器A客户端可能会突然发来大量请求,A的处理比较简单,A将请求发送给B,B接收处理的开销相对较大,一旦请求数目过多,B就会挂掉。使用一个条件队列来接收A发送给B的请求,这样无论A发送的请求数目有多少,B都可以按照自己的节奏来处理请求。

11.线程池

我们引入线程就是因为进程创建销毁的代价比较大,但是随着发展,客户端向服务器发送的请求可能呈指数增长,使用线程也觉得创建销毁的开销大了,所以引入线程池以及协程的概念,协程暂不讨论。
为什么引入线程池能够提高效率?因为创建销毁线程的操作主要是用户态以及内核态代码配合完成的工作,但是线程池先提前将线程创建好,然后建立好数据结构保存这些线程,需要线程直接拿,不需要了就放回去,这样的过程全是用户态的就节省了开销,避免与内核态交互。
另外线程池的使用主要分为Executors.newFixedThreadPool这种包装的线程池以及ThreadPoolExecutor这种标准的线程池的类,前者简单参数少,后者参数多更精细。

12.定时器

基本使用就是Timer类,然后构造对象就是需要两个参数,第一个是要执行的任务,第二个就是时间。构建这样的对象之后,定时器中的线程就会自动在你指定的第二个参数时间后去执行你指定的任务。

package Thread;import java.util.Timer;
import java.util.TimerTask;public class Demo39 {public static void main(String[] args) {Timer timer = new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println(3333);}}, 3333);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println(2222);}}, 2222);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println(1111);}}, 1111);}
}

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

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

相关文章

《MySQL对数据库中表的结构的操作》

文章目录 一、建表二、查看表结构所有能查看到数据库,表的操作痕迹的本质都是服务器保存下来了这些操作记录。 三、修改表1.改表名字2.添加表记录3.添加表的更多字段4.修改表的字段5. 删除表的字段 总结 以下的数据库表的操作全是基于user_db这个数据库操作的&#…

如何加入亚马逊云科技AWS特邀技领云合作博主

这一年来,小李哥帮助过上千名小伙伴学习AWS和考取认证,这次非常荣幸能和丸子爸比大哥、程序员学习日常成为第一批亚马逊AWS云领袖计划的特邀合作博主!感谢AWS培训与认证各位老师的支持,未来小李哥会继续坚持社区分享,带…

AC自动机

AC自动机 AC自动机有一个很出色的功能:实现多模式匹配。 多模式匹配:模式串有多个,主串只有一个,要进行多次模式串匹配。如果用KMP就要一个一个模式串进行匹配,效率低。AC自动机就可以做到,只要经过一些预…

【全网首出】npm run serve报错 Expression: thread_id_key != 0x7777

总结 困扰了一天!!!一直以为是自己哪里配置错了, 结果最后发现是node.js官方的问题, Node.js v16.x版本的fibers.node被弃用 本文阅读大概:3min #npm run serve时就报错 #找了一天的文章,找不…

# 从浅入深 学习 SpringCloud 微服务架构(八)Sentinel(1)

从浅入深 学习 SpringCloud 微服务架构(八)Sentinel(1) 一、sentinel:概述 1、前言 – 服务熔断 Hystrix 的替换方案。 1)2018年底 Netflix 官方宣布 Hystrix 已经足够稳定,不再积极开发 Hys…

JVM笔记2--垃圾收集算法

1、如何确认哪些对象“已死” 在上一篇文章中介绍到Java内存运行时的各个区域。其中程序计数器、虚拟机栈、本地方法栈3个区域随着线程而生,随线程而灭,栈中的栈帧随着方法的进入和退出而有条不紊的执行着入栈和出栈操作。每个栈帧中分配多少内存基本上…

组队竞赛和删除公共字符

这里附上两个题目的链接 题目一:删除公共字符_牛客题霸_牛客网 (nowcoder.com) 题目二:组队竞赛_牛客笔试题_牛客网 (nowcoder.com) 第一题 分析: 当我们看见这个题目的时候,可以使用传统的暴力查找方式,如判断第一个…

VsCode | 修改首页启动页 Logo

VsCode | 修改首页启动页 Logo 最终效果: 插件的安装 先安装插件 Custom CSS and JS Loader 插件配置 Ctrl Shift P 输入 打开用户设置,在末尾添加 "vscode_custom_css.imports": [""]下载 Logo 下载 Logo 点我下载 引入…

亚马逊云科技AWS免费证书-EC2服务器设计(含题库)

亚马逊云AWS官方程序员专属免费证书又来了!这次证书是关于AWS EC2实例的设计和搭建,EC2作为AWS服务的核心,是学好AWS的第一步。强推没有任何AWS背景和转码的小伙伴去学!学完也能变成AWS开发大神! 证书名字叫Getting St…

使用 TensorFlow 和 Keras 构建 U-Net

原文地址:building-a-u-net-with-tensorflow-and-keras 2024 年 4 月 11 日 计算机视觉有几个子学科,图像分割就是其中之一。如果您要分割图像,则需要在像素级别决定图像中可见的内容(执行分类时),或者从像…

Oracle23ai来了,23爱,全能、超级巨兽...

📢📢📢📣📣📣 作者:IT邦德 中国DBA联盟(ACDU)成员,10余年DBA工作经验, Oracle、PostgreSQL ACE CSDN博客专家及B站知名UP主,全网粉丝10万 擅长主流Oracle、My…

[图解]关于SysML v2(1)大刀阔斧 对比 伪创新圈子

1 00:00:03,960 --> 00:00:08,270 OMG在2月份,这里写了4月 2 00:00:08,440 --> 00:00:13,530 应该是2月,发布了 3 00:00:13,870 --> 00:00:17,700 SysML v2的 beta 2版本 4 00:00:17,870 --> 00:00:19,780 也是当前最新的版本 5 00:00:2…

ES的脑裂现象

目录 0 集群结点的职责1 什么是脑裂现象2 造成脑裂现象的原因2.1 网络问题(最常见)2.2 主节点负载过大,资源耗尽,别的结点ping不到主节点2.3 主节点JVM内存回收时间过长导致 3 脑裂现象的解决方案3.1 局域网部署3.2 角色分离&…

python实验一 简单的递归应用

实验一 实验题目 1、兔子繁殖问题(Fibonacci’s Rabbits)。一对兔子从出生后第三个月开始,每月生一对小兔子。小兔子到第三个月又开始生下一代小兔子。假若兔子只生不死,一月份抱来一对刚出生的小兔子,问一年中每个月各有多少只兔子。 &…

[每日AI·0501]GitHub 版 Devin,Transformer的强力挑战者 Mamba,Sora 制作细节与踩坑,OpenAI 记忆功能

AI 资讯 国资委:加快人工智能等新技术与制造全过程、全要素深度融合GitHub版 Devin 上线,会打字就能开发应用,微软 CEO:重新定义 IDE在12个视频理解任务中,Mamba 先打败了 TransformerSora 会颠覆电影制作吗&#xff…

Oracle 23c? No Oracle 23ai

昨天 Oracle 发布了最新的Oracle版本。出乎意料的是这个版本从Oracle 23c 更名为 Oracle 23ai ,似乎预示着Oracle的掌舵人Larry也要全面拥抱AI技术浪潮了。 23ai版本主要功能介绍: Oracle Database 23ai 是 Oracle 数据库的下一个长期支持版本。它包括 300 多项新功…

【LeetCode刷题】410. 分割数组的最大值

1. 题目链接2. 题目描述3. 解题方法4. 代码 1. 题目链接 410. 分割数组的最大值 2. 题目描述 3. 解题方法 题目中提到的是某个和的最大值是最小的,这种题目是可以用二分来解决的。 确定区间,根据题目的数据范围,可以确定区间就是[0, 1e9]…

LEETCODE LCR 041. 数据流中的移动平均值

class MovingAverage:def __init__(self, size: int):"""Initialize your data structure here."""self.sizesize1self.front0self.rear0self.queue[None for _ in range(size1)]self.sum0def next(self, val: int) -> float:# 满了if (self.…

postman中百度preview无法加载的解决方案

问题 在使用postman关联时,百度接口与天气接口已使用glb_city关联,但在百度接口发送请求时,发现preview无法加载 解决方案 1、进入百度 百度全球领先的中文搜索引擎、致力于让网民更便捷地获取信息,找到所求。百度超过千亿的中…

LeetCode面试298,二叉树最长连续序列(Python)

开始想着dfs,两种情况 1.以root为根 2.不以root为根 但是这样需要两个dfs分别进行,那么时间复杂度就上去了。 class Solution:def longestConsecutive(self, root: Optional[TreeNode]) -> int:def dfs(root):# 以root为根节点,可以延…