【JavaEE初阶】多线程上部

文章目录

  • 本篇目标:
  • 一、认识线程(Thread)
    • 1.概念:
    • 2.创建线程
  • 二、Thread 类及常见方法
    • 2.1 Thread 的常见构造方法
    • 2.2 Thread 的几个常见属性
    • 2.3 启动⼀个线程 - start()
    • 2.4 中断⼀个线程
    • 2.5 等待⼀个线程 - join()
    • 2.6 获取当前线程引用
    • 2.7 休眠当前线程 - sleep()
  • 三、线程的状态
    • 3.1 观察线程的所有状态
  • 四、 多线程带来的的风险-线程安全 (重点)
    • 4.1 线程安全的概念
    • 4.2 线程不安全的原因
  • 五、synchronized 关键字 - 监视器锁 monitor lock
    • 5.1 synchronized 的特性
    • 5.2 synchronized 使用示例
  • 总结


本篇目标:

  • 认识多线程
  • 掌握多线程程序的编写
  • 掌握多线程的状态
  • 掌握什么是线程不安全及解决思路
  • 掌握 synchronized关键字

提示:以下是本篇文章正文内容

一、认识线程(Thread)

1.概念:

1.1 线程是什么?

线程: ⼀个线程就是⼀个 “执行流”. 每个线程之间都可以按照顺序执行自己的代码. 多个线程之间 “同时” 执行着多份代码.

1.2 为什么要有线程?

首先, “并发编程” 成为 “刚需”.

  • 单核 CPU 的发展遇到了瓶颈. 要想提高算力, 就需要多核 CPU. 而并发编程能更充分利用多核 CPU资源.
  • 有些任务场景需要 “等待 IO”, 为了让等待 IO 的时间能够去做一些其他的工作, 也需要用到并发编程

其次, 虽然多进程也能实现 并发编程, 但是线程比进程更轻量.

  • 创建线程比创建进程更快.
  • 销毁线程比销毁进程更快.
  • 调度线程比调度进程更快

最后, 线程虽然比进程轻量, 但是人们还不满足, 于是又有了 “线程池”(ThreadPool) 和 “协程”(Coroutine)

1.3 进程和线程的区别

  • 进程是包含线程的. 每个进程至少有⼀个线程存在,即主线程。
  • 进程和进程之间不共享内存空间. 同⼀个进程的线程之间共享同⼀个内存空间
  • 进程是系统分配资源的最小单位,线程是系统调度的最小单位。
  • ⼀个进程挂了⼀般不会影响到其他进程. 但是⼀个线程挂了, 可能把同进程内的其他线程⼀起带走(整个进程崩溃).

1.4 Java的线程和操作系统线程的关系

  • 线程是操作系统中的概念. 操作系统内核实现了线程这样的机制, 并且对用户层提供了⼀些 API 供用户使用(例如 Linux 的 pthread 库).
  • Java 标准库中 Thread 类可以视为是对操作系统提供的 API 进行了进⼀步的抽象和封装

2.创建线程

方法一:继承 Thread 类

1.继承 Thread 来创建⼀个线程类.

class MyThread extends Thread {
	@Override
	public void run() {
 	System.out.println("这⾥是线程运⾏的代码");
 	}
}

2.创建 MyThread 类的实例

MyThread t = new MyThread();

3.调用 start 方法启动线程

t.start();		// 线程开始运⾏

方法二 实现 Runnable 接口

1.实现 Runnable 接口

class MyRunnable implements Runnable {
 	@Override
 	public void run() {
 	System.out.println("这⾥是线程运⾏的代码");
 	}
}

2.创建 Thread 类实例, 调用 Thread 的构造方法时将 Runnable 对象作为 target 参数.

Thread t = new Thread(new MyRunnable());

3.调用 start 方法

t.start();		// 线程开始运⾏

对比上面两种方法:
• 继承 Thread 类, 直接使用 this 就表示当前线程对象的引用.
• 实现 Runnable 接口, this 表示的是 MyRunnable 的引用. 需要使用Thread.currentThread()

其他变形:

  • 匿名内部类创建 Thread 子类对象
// 使⽤匿名类创建 Thread ⼦类对象
Thread t1 = new Thread() {
 	@Override
 	public void run() {
 	System.out.println("使⽤匿名类创建 Thread ⼦类对象");
 	}
};
  • 匿名内部类创建 Runnable 子类对象
// 使⽤匿名类创建 Runnable ⼦类对象
Thread t2 = new Thread(new Runnable() {
 	@Override
 	public void run() {
 	System.out.println("使⽤匿名类创建 Runnable ⼦类对象");
 	}
});
  • lambda 表达式创建 Runnable 子类对象
// 使⽤ lambda 表达式创建 Runnable ⼦类对象
Thread t3 = new Thread(() -> System.out.println("使⽤匿名类创建 Thread ⼦类对象"));
Thread t4 = new Thread(() -> {
 	System.out.println("使⽤匿名类创建 Thread 子类对象");
});

二、Thread 类及常见方法

Thread 类是 JVM 用来管理线程的⼀个类,换句话说,每个线程都有⼀个唯⼀的 Thread 对象与之关联。

2.1 Thread 的常见构造方法

方法说明
Thread()创建线程对象
Thread(Runnable target)使用Runnable 对象创建线程对象
Thread(String name)创建线程对象,并命名
Thread(Runnable target, String name)使用Runnable 对象创建线程对象,并命名
Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread("这是我的名字");
Thread t4 = new Thread(new MyRunnable(), "这是我的名字");

2.2 Thread 的几个常见属性

属性获取方法
IDgetld0)
名称getName()
状态getState()
优先级getPriority()
是否后台线程isDaemon()
是否存活isAlive()
是否被中断isInterrupted( )
  • ID 是线程的唯⼀标识,不同线程不会重复
  • 名称是各种调试工具用到
  • 状态表示线程当前所处的⼀个情况,下面我们会进⼀步说明
  • 优先级高的线程理论上来说更容易被调度到
  • 关于后台线程,需要记住⼀点:JVM会在⼀个进程的所有非后台线程结束后,才会结束运行。
  • 是否存活,即简单的理解,为 run 方法是否运行结束了
  • 线程的中断问题,下面我们进⼀步说明

2.3 启动⼀个线程 - start()

调用 start 方法, 才真的在操作系统的底层创建出⼀个线程。

2.4 中断⼀个线程

目前常见的有以下两种方式:

  1. 通过共享的标记来进行沟通
  2. 调用 interrupt() 方法来通知

2.5 等待⼀个线程 - join()

有时,我们需要等待⼀个线程完成它的工作后,才能进行自己的下⼀步工作。

方法说明
public void join()等待线程结束
public void join(long millis)等待线程结束,最多等millis毫秒
public void join(long millis, int nanos)同理,但可以更高精度

2.6 获取当前线程引用

方法说明
public static Thread currentTkread()返回当前线程对象的引用

2.7 休眠当前线程 - sleep()

方法
public static void sleep(long millis) throws InterruptedException
public static void sleep(long millis, int nanos)throws InterruptedException

三、线程的状态

3.1 观察线程的所有状态

线程的状态是⼀个枚举类型 Thread.State

  • NEW: 安排了工作, 还未开始行动
  • RUNNABLE: 可工作的. 又可以分成正在工作中和即将开始工作.
  • BLOCKED: 这几个都表示排队等着其他事情
  • WAITING: 这几个都表示排队等着其他事情
  • TIMED_WAITING: 这几个都表示排队等着其他事情
  • TERMINATED: 工作完成了

四、 多线程带来的的风险-线程安全 (重点)

4.1 线程安全的概念

如果多线程环境下代码运行的结果是符合我们预期的,即在单线程环境应该的结果,则说这个程序是线程安全的。

4.2 线程不安全的原因

  • **线程调度是随机的 **,这是线程安全问题的 罪魁祸首, 随机调度使⼀个程序在多线程环境下, 执行顺序存在很多的变数,程序猿必须保证在任意执行顺序下 , 代码都能正常工作。
  • 修改共享数据,多个线程修改同⼀个变量。
  • 原子性
  • 可见性
  • 指令重排序

【拓展】:Java 内存模型 (JMM): Java虚拟机规范中定义了Java内存模型
目的是屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到⼀致的并发效果。

  • 线程之间的共享变量存在 主内存 (Main Memory).
  • 每⼀个线程都有自己的 “工作内存” (Working Memory) .
  • 当线程要读取⼀个共享变量的时候, 会先把变量从主内存拷贝到工作内存, 再从工作内存读取数据.
  • 当线程要修改⼀个共享变量的时候, 也会先修改工作内存中的副本, 再同步回主内存.

由于每个线程有自己的工作内存, 这些工作内存中的内容相当于同⼀个共享变量的 “副本”. 此时修改线程1 的工作内存中的值, 线程2 的工作内存不⼀定会及时变化。

此时引柚柚们可能会有这些问题:

  • 为啥要整这么多内存?
  • 为啥要这么麻烦的拷来拷去?
  1. 为啥整这么多内存?
    实际并没有这么多 “内存”. 这只是 Java 规范中的⼀个术语, 是属于 “抽象” 的叫法.
    所谓的 “主内存” 才是真正硬件角度的 “内存”. 而所谓的 “工作内存”, 则是指 CPU 的寄存器和高速缓存。

  2. 为啥要这么麻烦的拷来拷去?
    因为 CPU 访问自身寄存器的速度以及高速缓存的速度, 远远超过访问内存的速度(快了 3 - 4 个数量级,也就是几千倍, 上万倍).

那么接下来问题又来了, 既然访问寄存器速度这么快, 还要内存干啥??
答案就是⼀个字: 贵
在这里插入图片描述

五、synchronized 关键字 - 监视器锁 monitor lock

5.1 synchronized 的特性

1) 互斥
synchronized 会起到互斥效果, 某个线程执行到某个对象的 synchronized 中时, 其他线程如果也执行到同⼀个对象 synchronized 就会阻塞等待.

  • 进入 synchronized 修饰的代码块, 相当于 加锁
  • 退出 synchronized 修饰的代码块, 相当于 解锁
    注意:
  • 上⼀个线程解锁之后, 下⼀个线程并不是立即就能获取到锁. 而是要靠操作系统来 “唤醒”. 这也就是操作系统线程调度的⼀部分工作.
  • 假设有 A B C 三个线程, 线程 A 先获取到锁, 然后 B 尝试获取锁, 然后 C 再尝试获取锁, 此时 B 和 C都在阻塞队列中排队等待. 但是当 A 释放锁之后, 虽然 B 比 C 先来的, 但是 B 不⼀定就能获取到锁,而是和 C 重新竞争, 并不遵守先来后到的规则.
    2) 可重入
    synchronized 同步块对同⼀条线程来说是可重入的,不会出现自己把自己锁死的问题。

5.2 synchronized 使用示例

synchronized 本质上要修改指定对象的 “对象头”. 从使用角度来看, synchronized 也势必要搭配⼀个具体的对象来使用。

1) 修饰代码块: 明确指定锁哪个对象。
锁任意对象

public class SynchronizedDemo {
 	private Object locker = new Object();
 
 	public void method() {
 		synchronized (locker) {
 		}
 	}
}

锁当前对象

public class SynchronizedDemo {
 	public void method() {
 		synchronized (this) {
 		}
 	}
}

2) 直接修饰普通方法: 锁的 SynchronizedDemo 对象

public class SynchronizedDemo {
 	public synchronized void methond() {
 	}
}

3) 修饰静态方法: 锁的 SynchronizedDemo 类的对象

public class SynchronizedDemo {
 	public synchronized static void method() {
 	}
}

我们重点要理解,synchronized 锁的是什么. 两个线程竞争同一把锁, 才会产生阻塞等待

总结

多线程几乎是面试必问题,柚柚们一定要好好理解喔!!!
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[vulnhub] Corrosion: 2

https://www.vulnhub.com/entry/corrosion-2,745/ 提示:枚举才是神 主机发现端口扫描 使用nmap扫描网段类存活主机 因为靶机是我最后添加的,所以靶机IP是6 ,kali是10 nmap -sP 192.168.56.0/24 Starting Nmap 7.94SVN ( https://nmap.org ) …

MySQL数据库的备份与还原

目录 mysql 数据库的备份 生成SQL脚本 1 在控制台使用mysqldump命令可以用来生成指定数据库的脚本 ​编辑2 在数据库图形化界面工具:DateGrip 中操作:导出 mysql 数据库的还原 执行SQL脚本 1 在控制台使用 命令,指定将sql脚本导入到指定…

ASR 点亮闪光灯和后摄对焦马达

ASR翱捷科技 ASR kernel 5.10 android14 ASR EVB平台 ASR 原理图 闪光灯是gpio控制 1.驱动 路径:asr\kernel\linux\drivers\media\platform\asr-mars11\flash\leds-gpio-flash.c 驱动加载后生成设备节点/sys/class/leds/torch 和/sys/class/leds/flash。 Makefile Kconfig…

【大数据学习 | HBASE】hbase的读数据流程与hbase读取数据

1. hbase的读数据流程 在解析读取流程之前我们还需要知道两个功能性的组件和HFIle的格式信息 HFILE 存储在hdfs中的hbase文件,这个文件中会存在hbase中的数据以kv类型显示,同时还会存在hbase的元数据信息,包括整个hfile文件的索引大小&…

【GPTs】MJ Prompt Creator:轻松生成创意Midjourney提示词

博客主页: [小ᶻZ࿆] 本文专栏: AIGC | GPTs应用实例 文章目录 💯GPTs指令💯前言🚀 MJ Prompt Creator主要功能适用场景优点缺点 💯 小结 💯GPTs指令 中文翻译: 任务说明 您是一款为幻灯片…

实现3D热力图

实现思路 首先是需要用canvas绘制一个2D的热力图,如果你还不会,请看json绘制热力图。使用Threejs中的canvas贴图,将贴图贴在PlaneGeometry平面上。使用着色器材质,更具json中的数据让平面模型 拔地而起。使用Threejs内置的TWEEN&…

ATom: 矿物粉尘在卷云形成中的主导作用

目录 ATom: Dominant Role of Mineral Dust in Cirrus Cloud Formation 简介 摘要 代码 引用 网址推荐 知识星球 机器学习 ATom: Dominant Role of Mineral Dust in Cirrus Cloud Formation 简介 该数据集提供了:(1)由 NOAA 粒子分析…

C语言 | Leetcode C语言题解之第560题和为K的子数组

题目: 题解: // 暴力美学:20行C代码 int subarraySum(int *nums, int numsSize, int k) {int count 0;// 弄个大数组做个暴力的Hash表,大概4*20M*2160M。用calloc初始化为全零。int *maps (int *)calloc(1001 * 20001 * 2, siz…

基于vue框架的的社区居民服务管理系统8w86o(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能:居民,楼房信息,报修信息,缴费信息,维修进度 开题报告内容 基于Vue框架的社区居民服务管理系统开题报告 一、研究背景与意义 随着城市化进程的加速,社区居民数量激增,社区管理面临着前所未有的挑战。传统的社区…

UE5.4 PCG 自定义PCG蓝图节点

ExecuteWithContext: PointLoopBody: 效果:点密度值与缩放成正比

[ Linux 命令基础 2 ] Linux 命令详解-系统管理命令

🍬 博主介绍 👨‍🎓 博主介绍:大家好,我是 _PowerShell ,很高兴认识大家~ ✨主攻领域:【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 🎉点赞➕评论➕收藏 养成习…

单链表算法题(数据结构)

1. 反转链表 https://leetcode.cn/problems/reverse-linked-list/description/ 题目: 看到这个题目的时候我们怎么去想呢?如果我们反应快的话,应该可以想到我们可以从1遍历到5然后依次头插,但是其实我们还有更好的办法&#xff…

Java代码审计-模板注入漏洞

一、模板引擎 在Java开发当中,为了将前端和后端进行分离,降低项目代码的耦合性,使代码更加易于维护和管理。除去以上的原因,模板引擎还能实现动态和静态数据的分离。 二、主流模板引擎 在Java中,主流的模板引擎有:Fre…

SQLI LABS | Less-39 GET-Stacked Query Injection-Intiger Based

关注这个靶场的其它相关笔记:SQLI LABS —— 靶场笔记合集-CSDN博客 0x01:过关流程 输入下面的链接进入靶场(如果你的地址和我不一样,按照你本地的环境来): http://localhost/sqli-labs/Less-39/ 本关是堆…

《深度学习》——深度学习基础知识(全连接神经网络)

文章目录 1.神经网络简介2.什么是神经网络3.神经元是如何工作的3.1激活函数3.2参数的初始化3.2.1随机初始化3.2.2标准初始化3.2.3Xavier初始化(tf.keras中默认使用的)3.2.4He初始化 4.神经网络的搭建4.1通过Sequential构建神经网络4.2通过Functional API…

备战软考Day05-数据库系统基础知识

一、基本概念 1.数据库 数据库(Database,缩写为DB)是指长期存储在计算机内的、有组织的、可共享的数据集合。数据库中的数据按一定的数据模型组织、描述和存储,具有较小的冗余度、较高的数据独立性和易扩展性,并可为各种用户共享。 2.数据…

Spark 的容错机制:保障数据处理的稳定性与高效性

Spark 的介绍与搭建:从理论到实践_spark环境搭建-CSDN博客 Spark 的Standalone集群环境安装与测试-CSDN博客 PySpark 本地开发环境搭建与实践-CSDN博客 Spark 程序开发与提交:本地与集群模式全解析-CSDN博客 Spark on YARN:Spark集群模式…

NLP论文速读(NeurIPS2024)|使用视觉增强的提示来增强视觉推理

论文速读|Enhancing LLM Reasoning via Vision-Augmented Prompting 论文信息: 简介: 这篇论文试图解决的问题是大型语言模型(LLMs)在处理包含视觉和空间线索的推理问题时的局限性。尽管基于LLMs的推理框架(如Chain-of-Thought及其…

在 RHEL 8 | CentOS Linux release 8.5.2111上安装 Zabbix 6

1. 备份YUM源文件 cd /etc/yum.repos.d/ mkdir bak mv C* ./bak/ wget -O /etc/yum.repos.d/CentOS-Linux-BaseOS.repo https://mirrors.aliyun.com/repo/Centos-vault-8.5.2111.repo yum clean all yum makecache2. 将 SELinux 设置为宽容模式,如下所示。 sudo s…

在 Mac 和 Windows 系统中快速部署 OceanBase

OceanBase 是一款分布式数据库,具备出色的性能和高扩展性,可以为企业用户构建稳定可靠、灵活扩展性能的数据库服务。本文以开发者们普遍熟悉的Windows 或 Mac 环境为例,介绍如何快速上手并体验OceanBase。 一、环境准备 1. 硬件准备 OceanB…