【Java】线程相关面试题 (基础)
文章目录
- 线程与进程区别
- 并行与并发区别解析
- 概念含义
- 资源利用
- 执行方式
- 应用场景
- 创建线程
- 线程状态
- 如何保证新建的三个线程按顺序执行
- wait方法和sleep方法的不同
- 所属类和使用场景
- 方法签名和参数说明
- 调用`wait`方法的前提条件
- 被唤醒的方式
- 与`notify`/`notifyAll`方法的协作
- 使用示例
- 注意事项
- 停止线程的三种方式
线程与进程区别
- 进程的定义与实例
- 进程是当程序被运行,从磁盘加载程序代码到内存时开启的。例如打开谷歌浏览器或txt文档等程序就是开启了一个进程,在Windows中有多实例进程(可打开多份,如浏览器、txt文档)和单实例进程(如tears客户端、企业微信在系统层面只能打开一份)。
- 线程的定义与作用
- 线程包含指令,交给CPU运行。进程至少包含一到多个线程,每个线程执行不同任务。
- 线程与进程的区别
- 进程是正在运行的程序实例,包含多个线程执行不同任务。不同进程使用不同内存空间,而当前进程下的所有线程可以共享该进程的内存空间。线程更轻量,其上下文切换成本一般比进程上下文切换成本低。面试时主要回答这三点:进程和线程的关系、内存占用情况(强调进程下线程共享内存)、线程更轻量且切换成本低。
并行与并发区别解析
概念含义
- 并行:同一时间动手做多件事情的能力。例如在多核CPU下,多个核心可以同时执行不同的线程,像四核CPU能同时执行四个线程,这些线程是真正意义上的同时进行。
- 并发:同一时间应对多件事情的能力。在单核CPU中,由于只有一个核心,多个线程不能同时执行,而是通过任务管理器分配时间片,轮流使用CPU,虽然每个时间片只有一个线程执行,但因CPU切换速度快,宏观上给人一种并行的感觉,微观上实际是串行执行。例如家庭主妇独自做饭、打扫卫生、给孩子喂奶,一个人轮流交替做这些事,就如同单核CPU处理多线程任务。
资源利用
- 并行:需要多核CPU等硬件资源支持,每个核心可以独立运行一个线程,实现真正的同时处理多个任务,充分利用了多核CPU的计算能力,提高了整体任务处理效率。
- 并发:主要依赖于操作系统的调度机制,在单核CPU环境下,通过合理分配时间片给不同线程,让多个任务看起来像是同时在处理,有效利用了单个CPU的时间资源,避免某个线程长时间占用CPU导致其他线程等待过久,但整体效率受限于单核CPU的处理能力。
执行方式
- 并行:多个任务在多个处理器或多核CPU的不同核心上同时执行,任务之间相互独立,不存在资源竞争(除非访问共享资源时需要进行同步处理),执行顺序是真正意义上的同时进行。
- 并发:多个任务在单核CPU上通过时间片轮转的方式交替执行,每个任务执行一段时间后暂停,切换到下一个任务,由于时间片很短,给用户造成任务在同时进行的错觉,但实际上在微观层面是串行执行的,任务之间可能存在频繁的上下文切换开销。
应用场景
- 并行:适用于计算密集型任务,如大规模数据处理、复杂科学计算等,通过将任务分解到多个核心上同时计算,可以显著缩短计算时间,提高计算性能。例如在图像渲染、视频编码解码等领域,利用多核CPU并行处理不同部分的图像或视频数据,能快速完成处理工作。
- 并发:常用于I/O密集型任务,如网络通信、文件读写等操作,这些任务在等待I/O操作完成时会阻塞线程,使用并发可以在等待一个任务的I/O操作时切换到其他任务执行,提高CPU利用率,避免线程长时间空闲等待。比如在一个Web服务器中,同时处理多个客户端的请求,每个请求在等待数据库查询或文件读取等I/O操作时,服务器可以切换去处理其他客户端请求,提高整体响应能力。
创建线程
-
创建线程的方式介绍
- 创建线程共有四种方式,分别是继承Thread类、实现Runnable接口、实现Callable接口和使用线程池创建线程。
-
继承Thread类创建线程
- 定义一个类继承Thread类,重写run方法,在run方法中编写线程要执行的代码。
- 使用时先创建该类的对象,然后调用start方法开启线程,new两次对象相当于开两个线程。
-
实现Runnable接口创建线程
- 定义一个类实现Runnable接口,重写run方法,该方法为线程执行的代码。
- 使用时先创建类的对象,将其包装在Thread类中,再调用Thread对象的start方法开启线程,new两次对象相当于开两个线程。
-
实现Callable接口创建线程
- 定义一个类实现Callable接口,重写call方法,call方法有返回值(通过泛型指定)且可抛异常,call方法的代码为线程要执行的逻辑。
- 使用时先创建类的对象,配合FutureTask包装该对象,再将FutureTask包装在Thread类中,调用Thread对象的start方法开启线程,通过FutureTask的get方法获取线程执行后的返回值。
-
使用线程池创建线程
- 创建一个类实现Runnable或Callable接口,编写线程执行逻辑。
- 使用时先创建固定大小的线程池(线程池后期会详细讲解),通过线程池的submit方法提交任务(即实现接口的类的对象),线程池会自动执行线程中的逻辑。
-
Runnable和Callable的区别
- 返回值:Runnable接口的run方法无返回值,Callable接口的call方法有返回值且需配合FutureTask使用get方法获取返回值。
- 异常处理:run方法不能抛异常,只能内部try - catch处理;call方法可以抛异常。
-
start方法和run方法的区别
- 功能:start方法用于启动线程,线程独立执行run方法中的代码;run方法是普通方法,直接调用如同调用普通方法,在当前线程顺序执行代码。
- 调用次数:start方法只能被调用一次启动线程,多次调用会抛异常;run方法可多次调用。
-
总结
- 创建线程有继承Thread类、实现Runnable接口、实现Callable接口和使用线程池四种方式,项目中一般使用线程池创建线程。
- Runnable和Callable的区别主要在返回值、异常处理方面。
- start方法用于启动线程且只能调用一次,run方法是普通方法可多次调用。
线程状态
- 线程状态面试题介绍
- 状态定义:参考Thread类的内部枚举类State,定义了六个线程状态,即new(新建)、runnable(可运行)、block(阻塞)、waiting(等待)、time waiting(时间等待)、terminated(终结)。
- 线程状态及转换初步讲解
- 新建状态:创建线程对象时进入,如创建线程t1和t2时。
- 就绪与运行状态:调用线程方法后进入就绪状态,抢到CPU时间片才有执行权,线程运行完成后进入死亡状态。
- 阻塞状态:线程加锁时,未获得锁的线程进入阻塞状态,获得到锁后转为可运行状态。
- 等待状态:线程内部调用wait方法进入等待状态,其他线程调用notify或notifyAll方法唤醒后变为可运行状态。
- 时间等待状态:线程调用sleep方法进入时间等待状态,时间结束后转为可运行状态。
- 总结线程状态及转换
- 线程状态总结:包含六个状态,新建、可运行、阻塞、等待、计时等待、终止状态。
- 状态转换关系梳理
- 新建到可执行:创建线程对象为新建状态,调用方法后转换为可执行状态。
- 可执行到终止:线程获取CPU执行权并执行结束后为终止状态。
- 可执行状态的其他转换
- 可执行到阻塞:未获取到锁(如synchronized或Lock锁)进入阻塞状态,获得到锁的执行权后切换为可执行状态。
- 可执行到等待:调用wait方法进入等待状态,其他线程调用notify或notifyAll唤醒后切换为可执行状态。
- 可执行到计时等待:调用sleep方法进入计时等待状态,时间到后切换为可执行状态。
文章目录
- 线程与进程区别
- 并行与并发区别解析
- 概念含义
- 资源利用
- 执行方式
- 应用场景
- 创建线程
- 线程状态
- 如何保证新建的三个线程按顺序执行
- wait方法和sleep方法的不同
- 所属类和使用场景
- 方法签名和参数说明
- 调用`wait`方法的前提条件
- 被唤醒的方式
- 与`notify`/`notifyAll`方法的协作
- 使用示例
- 注意事项
- 停止线程的三种方式
如何保证新建的三个线程按顺序执行
- 方法介绍
- 对于“如何保证新建t1、t2、t3三个线程按顺序执行”这一面试题,可使用线程中的“join方法”来解决。该方法的作用是等待线程运行结束,调用此方法的线程会被阻塞,进入time waiting(时间等待)状态,直到被调用“join方法”的线程执行完成后,调用者才能继续执行。
- 代码演示
- 在代码中创建了t1、t2、t3三个线程,在t2线程中调用了t1的“join方法”,这意味着t2线程想要运行必须等待t1线程结束;在t3线程中调用了t2的“join方法”,所以t3线程需等待t2线程运行结束后才能运行。启动线程的顺序不影响最终结果,最终会按t1、t2、t3的顺序执行。通过代码执行结果展示了t1先执行,完成后t2执行,t2执行完t3执行,从而验证了这种方法可保证线程按顺序执行。
-
notify和notifyAll的区别
为notify是只随机唤醒一个等待(wait)方法的线程,而notifyAll是唤醒所有等待方法的线程。
wait方法和sleep方法的不同
在Java中,wait
方法用于使当前线程等待,直到其他线程调用该对象的notify
方法或notifyAll
方法唤醒它,或者等待一定的时间(如果指定了超时时间)。以下是关于wait
方法的详细介绍:
所属类和使用场景
- 所属类:
wait
方法属于Object
类,这意味着Java中的任何对象都可以调用该方法。 - 使用场景:主要用于多线程编程中,实现线程之间的协作和同步。例如,当一个线程需要等待某个条件满足时,可以调用
wait
方法进入等待状态,直到其他线程改变了共享资源的状态并通知它。
方法签名和参数说明
- 方法签名:
public final void wait() throws InterruptedException
和public final native void wait(long timeout) throws InterruptedException
。 - 参数说明
- 无参的
wait
方法会使当前线程无限期等待,直到被唤醒。 - 带参数的
wait
方法接受一个long
类型的参数,表示等待的超时时间(以毫秒为单位)。如果在指定时间内没有被唤醒,线程会自动苏醒并继续执行。
- 无参的
调用wait
方法的前提条件
- 当前线程必须拥有该对象的锁。也就是说,
wait
方法必须在同步代码块(synchronized
块)中调用,否则会抛出IllegalMonitorStateException
异常。 - 例如,以下代码演示了正确调用
wait
方法的方式:
synchronized (object) {// 当前线程获取了object对象的锁,可以调用wait方法object.wait();
}
被唤醒的方式
- 其他线程调用
notify
方法:唤醒在此对象监视器上等待的单个线程。如果有多个线程在等待,选择是任意的,由操作系统的调度策略决定。 - 其他线程调用
notifyAll
方法:唤醒在此对象监视器上等待的所有线程。被唤醒的线程将竞争重新获取对象的锁,然后继续执行。 - 等待超时:如果调用了带超时参数的
wait
方法,当超时时间到达时,线程会自动苏醒,继续执行后续代码。
与notify
/notifyAll
方法的协作
wait
方法与notify
/notifyAll
方法必须在同一对象上调用,以实现线程之间的正确协作。- 通常,一个线程在等待某个条件时调用
wait
方法,而另一个线程在改变条件后调用notify
或notifyAll
方法来唤醒等待的线程。
使用示例
以下是一个简单的示例,展示了wait
方法和notify
方法的基本用法:
public class WaitNotifyExample {public static void main(String[] args) {final Object lock = new Object();// 线程1:等待条件满足Thread thread1 = new Thread(() -> {synchronized (lock) {try {System.out.println("线程1:开始等待");lock.wait(); // 释放锁并等待System.out.println("线程1:被唤醒,继续执行");} catch (InterruptedException e) {e.printStackTrace();}}});// 线程2:改变条件并通知线程1Thread thread2 = new Thread(() -> {synchronized (lock) {System.out.println("线程2:改变条件,并通知线程1");lock.notify(); // 唤醒等待的线程1}});thread1.start();try {Thread.sleep(1000); // 确保线程1先进入等待状态} catch (InterruptedException e) {e.printStackTrace();}thread2.start();}
}
在上述示例中,线程1获取lock
对象的锁后调用wait
方法进入等待状态,同时释放锁。线程2获取lock
对象的锁后调用notify
方法唤醒线程1,线程1被唤醒后重新竞争锁,获取锁后继续执行后续代码。
注意事项
- 在使用
wait
方法时,必须在循环中调用,以避免虚假唤醒(spurious wakeup)的问题。虚假唤醒是指线程在没有被其他线程明确唤醒的情况下苏醒,可能是由于操作系统或JVM的内部原因。例如:
while (condition) {synchronized (object) {object.wait();}
}
- 调用
wait
方法的线程会释放对象的锁,但在被唤醒后重新竞争锁。如果多个线程同时竞争锁,唤醒顺序是不确定的,取决于操作系统的调度策略。 wait
方法会抛出InterruptedException
异常,当线程在等待过程中被中断时,会抛出该异常。因此,在调用wait
方法时,需要正确处理异常,以确保程序的稳定性和正确性。
文章目录
- 线程与进程区别
- 并行与并发区别解析
- 概念含义
- 资源利用
- 执行方式
- 应用场景
- 创建线程
- 线程状态
- 如何保证新建的三个线程按顺序执行
- wait方法和sleep方法的不同
- 所属类和使用场景
- 方法签名和参数说明
- 调用`wait`方法的前提条件
- 被唤醒的方式
- 与`notify`/`notifyAll`方法的协作
- 使用示例
- 注意事项
- 停止线程的三种方式
停止线程的三种方式
- 停止线程的三种方式
- 使用退出标志:通过定义一个标志变量(如
flag
),在run
方法中使用循环条件控制线程执行,当标志变量改变时,线程正常退出。例如,在my interrupt 1
类中,run
方法里使用while
循环(while (!flag)
),线程在循环内打印信息并睡眠3秒。主线程启动该线程后睡眠6秒,然后将flag
改为true
,使线程在6秒后正常退出。 - 调用stop方法(不推荐):
stop
方法可以强行终止线程,但此方法已作废不推荐使用。 - 调用interrupt方法:该方法包含两种情况。
- 一是打断阻塞的线程(如处于
sleep
、wait
、join
状态的线程),调用interrupt
会抛出InterruptException
异常; - 二是打断正常的线程,可根据线程的打断状态标记是否退出线程,与第一种使用退出标志的方式类似。
- 一是打断阻塞的线程(如处于
- 使用退出标志:通过定义一个标志变量(如
相关文章:

【Java】线程相关面试题 (基础)
文章目录 线程与进程区别并行与并发区别解析概念含义资源利用执行方式应用场景 创建线程线程状态如何保证新建的三个线程按顺序执行wait方法和sleep方法的不同所属类和使用场景方法签名和参数说明调用wait方法的前提条件被唤醒的方式与notify/notifyAll方法的协作使用示例注意事…...

【数字化】华为一体四面细化架构蓝图
导读:华为的“一体四面”企业架构设计方法是一种综合性的管理框架,它通过业务架构、信息架构、应用架构和技术架构的集成设计,构建出一个既符合业务需求,又具备高度灵活性和可扩展性的IT系统。这种架构设计方法强调从业务视角出发…...

frameworks 之 WMS添加窗口流程
frameworks 之 触摸事件窗口查找 1.获取WindowManager对象2.客户端添加view3. 服务端添加view (NO_SURFACE)4.重新布局 (DRAW_PENDING)4.1 创建 SurfaceControl 5.通知绘制 (COMMIT_DRAW_PENDING, READY_TO_SHOW, HAS_DRAWN)5. 1 布局测量和刷新 6.总结 …...

搜索方法归类全解析
搜索方法归类全解析 搜索方法是人工智能和计算机科学中用于解决问题、优化路径或发现数据模式的关键技术。根据不同的标准,搜索方法可以被分为多种类别。本文将详细介绍这些分类标准,并探讨每一类的特点及其代表算法,同时补充更多关于搜索的相…...

第1关:简易考试系统之用户注册
任务描述 本关任务:实现简易考试系统中新用户注册的功能。 编程要求 仔细阅读右侧编辑区内给出的代码框架及注释,在 Begin-End 中实现简易考试系统中新用户注册的功能,具体要求如下: User.java 提供了用户的基本信息,…...

VMware的三种网络模式——在NAT模式下开放接口为局域网内其他主机提供服务
众所周知 VMware 有三种常用的网络通讯模式,分别是:Bridged(桥接模式)、NAT(网络地址转换模式)、Host-Only(仅主机模式),它们各有不同的用法。 Bridged 桥接模式是与主机…...

智慧地下采矿:可视化引领未来矿业管理
图扑智慧地下采矿可视化平台通过整合多源数据,提供实时 3D 矿井地图及分析,提升了矿产开采的安全性与效率,为矿业管理提供数据驱动的智能决策支持,推动行业数字化转型。...

流量主微信小程序工具类去水印
工具类微信小程序流量主带后台管理,可开通广告,带自有后台管理,不借助第三方接口 介绍 支持抖音,小红书,哔哩哔哩视频水印去除,功能实现不借助第三方平台。可实现微信小程序流量主广告变现功能,…...

代码随想录算法【Day5】
DAY5 1.熟悉哈希表的数据结构:数组、map和set,使用方法、使用场景 2.哈希表应用场景:解决给你一个元素,判断它在集合里是否出现过。 242.有效的字母异位词 本题用数组解决的。 class Solution { public:bool isAnagram(strin…...

Leetcode 3403. Find the Lexicographically Largest String From the Box I
Leetcode 3403. Find the Lexicographically Largest String From the Box I 1. 解题思路2. 代码实现 题目链接:3403. Find the Lexicographically Largest String From the Box I 1. 解题思路 这一题我一开始的思路是想用动态规划,结果发现想复杂了&…...

【游戏设计原理】36 - 环境叙事
一、 分析并总结 核心要点 环境叙事的本质:将游戏的设定视为叙事的一部分,利用环境元素(如物品、对话、视觉效果等)传递故事和信息。世界设定的重要性:一个强大的世界设定可以像角色一样,驱动叙事并增强玩…...

Python 中的 lambda 函数和嵌套函数
Python 中的 lambda 函数和嵌套函数 Python 中的 lambda 函数和嵌套函数Python 中的 lambda 函数嵌套函数(内部函数)封装辅助函数闭包和工厂函数 Python 中的 lambda 函数和嵌套函数 Python 中的 lambda 函数 Lambda 函数是基于单行表达式的匿名函数。…...

语言模型评价指标
1. BLEU(Bilingual Evaluation Understudy) 目标:衡量生成文本和参考文本之间的词汇相似性。 计算步骤: N-gram 匹配: 将生成文本和参考文本分解成 1-gram、2-gram、…、N-gram(通常取到 4-gramÿ…...

工程师 - MSYS2介绍
https://www.msys2.org/ MSYS2 是一系列工具和库,为您提供了一个易于使用的环境,用于构建、安装和运行本地 Windows 软件。 MSYS2 is a collection of tools and libraries providing you with an easy-to-use environment for building, installing an…...

算法基础三:插入排序
定义 插入排序(英语:Insertion Sort)是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用…...

小米汽车加速出海,官网建设引领海外市场布局!
面对国内市场的饱和态势,中国企业出海步伐纷纷加速,小米也是其中的一员。Canalys数据显示,2024年第三季度,小米以13.8%的市场份额占比,实现了连续17个季度位居全球前三的成绩。 据“36 氪汽车”报道,小米汽…...

Python Polars快速入门指南:LazyFrames
前文已经介绍了Polars的Dataframe, Contexts 和 Expressions,本文继续介绍Polars的惰性API。惰性API是该库最强大的功能之一,使用惰性API可以设定一系列操作,而无需立即运行它们。相反,这些操作被保存为计算图,只在必要…...

什么是网络安全(Cybersecurity)?
不同组织机构对网络安全(Cybersecurity或Cyber Security)的定义不尽相同。从目标上来说,网络安全主要用于保护网络、计算机、移动设备、应用程序及数据等资产免受网络攻击,避免造成数据泄露、业务中断等安全问题。 网络钓鱼、勒索…...

VBA批量插入图片到PPT,一页一图
Sub InsertPicturesIntoSlides()Dim pptApp As ObjectDim pptPres As ObjectDim pptSlide As ObjectDim strFolderPath As StringDim strFileName As StringDim i As Integer 设置图片文件夹路径strFolderPath "C:\您的图片文件夹路径\" 请替换为您的图片文件夹路径…...

Pandas-DataFrame入门
文章目录 一. Pandas DataFrame简介二. 加载数据集1. 目的2. 步骤① 导包② 加载csv③ 查看数据类型及属性④ Pandas与Python常用数据类型对照 三. 查看部分数据1. 根据列名加载部分列数据① 加载一列数据,通过df[列名]方式获取② 加载多列数据,通过df[[…...

爬虫 - 爬取王者荣耀所有皮肤图片
结果展示 安装 pip install requests logger代码 import json import os import re from concurrent.futures import ThreadPoolExecutorimport requests from loguru import loggerdef parse_url(url, bFalse):try:headers {"User-Agent": "Mozilla/5.0 (Wi…...

【畅购商城】购物车模块之查看购物车
目录 分析 接口 后端实现 前端实现:显示页面 前端实现:显示购物车信息 分析 用户如果没有登录,购物车存放在浏览器端的localStorage处,且以数组的方式进行存储。用户如果登录了,购物车存放在redis中,…...

Spring Boot 学习笔记
学习代码第一步:如何写 Hello world ? 1、新建项目 新建一个 Maven Java 工程,在 pom.xml 文件中添加 Spring Boot Maven 依赖: <parent><groupId>org.springframework.boot</groupId><artifactId>spri…...

快速打造智能应用:从设计到上线的全流程指南
随着人工智能技术的快速发展,如何将大模型技术转化为实际应用成为了各行业关注的焦点。本文将以一个经典的 RAG(检索增强生成)知识问答系统为例,详细介绍从智能体设计到最终应用部署的全流程。通过结合阿里云的魔笔低代码平台和丰…...

Java-将一个大列表均分成多个小列表,每个小列表包含10个元素
要将一个大列表均分成多个小列表,每个小列表包含10个元素,可以使用多种方法。以下是几种常 见的方法: 方法一:使用 subList 这是你已经提到的方法,通过 subList 来获取子列表。 import java.util.ArrayList; import java.util.List;public class BatchProcessingExamp…...

tcp_rcv_synsent_state_process函数
tcp_rcv_synsent_state_process 是 Linux Kernel 中用于处理 TCP 连接在 SYN-SENT 状态下接收到报文的函数。这个函数在 TCP 三次握手阶段起到了至关重要的作用,处理了在客户端发送 SYN 请求之后收到服务器响应报文的各种情况。 以下是这个函数的解读和剖析: int tcp_rcv_sy…...

关于无线AP信道调整的优化(锐捷)
目录 一、信道优化的基本原则二、2.4G频段信道优化三、5G频段信道优化四、信道优化代码具体示例五、其他优化措施 一、信道优化的基本原则 信道优化旨在减少信道间的干扰,提高网络覆盖范围和信号质量。基本原则包括: 1. 选择合适的信道:根据…...

C#编写的金鱼趣味小应用 - 开源研究系列文章
今天逛网,在GitHub中文网上发现一个源码,里面有这个金鱼小应用,于是就下载下来,根据自己的C#架构模板进行了更改,最终形成了这个例子。 1、 项目目录; 2、 源码介绍; 1) 初始化; 将样…...

计算机网络|数据流向剖析与分层模型详解
文章目录 一、网络中的数据流向二、计算机网络通信模型1.OSI 模型2.TCP/IP 模型3.TCP/IP五层模型3.1 分层架构描述3.2各层地址结构3.3UDP数据包报头结构 三、总结 一、网络中的数据流向 在计算机网络中,数据的流向是指数据从发送端到接收端的传输路径。数据流向涉及…...

某些iphone手机录音获取流stream延迟问题 以及 录音一次第二次不录音问题
一些型号的iphone手机录音获取流stream延迟问题 以及 录音一次第二次不录音问题 延迟问题 navigator.mediaDevices.getUserMedia({ audio: true }) .then((stream) > {console.log(stream) })从开始到获取stream会有将近2s的延迟 导致按下按钮开始录音 会有前…...