线程基础知识

目录

进程

线程

CPU 核心数和线程数的关系

上下文切换(Context switch)

Thread 和 Runnable 的区别

Callable、Future 和 FutureTask

面试题:新启线程有几种方式?

中止

中断

深入理解 run()和 start()


进程

我们常听说的是应用程序,也就是 app,由指令和数据组成。但是当我们不 运行一个具体的 app 时,这些应用程序就是放在磁盘(也包括 U 盘、远程网络 存储等等)上的一些二进制的代码。一旦我们运行这些应用程序,指令要运行, 数据要读写,就必须将指令加载至 CPU,数据加载至内存。在指令运行过程中 还需要用到磁盘、网络等设备,从这种角度来说,进程就是用来加载指令、管理 内存、管理 IO 的。

当一个程序被运行,从磁盘加载这个程序的代码至内存,这时就开启了一个 进程。

进程就可以视为程序的一个实例。大部分程序可以同时运行多个实例进程 (例如记事本、画图、浏览器 等),也有的程序只能启动一个实例进程(例如 网易云音乐、360 安全卫士等)。显然,程序是死的、静态的,进程是活的、动态 的。进程可以分为系统进程和用户进程。凡是用于完成操作系统的各种功能的进 程就是系统进程,它们就是处于运行状态下的操作系统本身,用户进程就是所有由 你启动的进程。

站在操作系统的角度,进程是程序运行资源分配(以内存为主)的最小单位。

线程

一个机器中肯定会运行很多的程序,CPU 又是有限的,怎么让有限的 CPU 运行这么多程序呢?就需要一种机制在程序之间进行协调,也就所谓 CPU 调度。 线程则是 CPU 调度的最小单位。

线程必须依赖于进程而存在,线程是进程中的一个实体,是 CPU 调度和分 派的基本单位,它是比进程更小的、能独立运行的基本单位。线程自己基本上不 拥有系统资源,,只拥有在运行中必不可少的资源(如程序计数器,一组寄存器和栈), 但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。一个进程可 以拥有多个线程,一个线程必须有一个父进程。线程,有时也被称为轻量级进程 (Lightweight Process,LWP),早期 Linux 的线程实现几乎就是复用的进程,后来 才独立出自己的 API。

Java 线程的无处不在
Java 中不管任何程序都必须启动一个 main 函数的主线程; Java Web 开发里

面的定时任务、定时器、JSP 和 Servlet、异步消息处理机制,远程访问接口 RM 等, 任何一个监听事件,onclick 的触发事件等都离不开线程和并发的知识。

CPU 核心数和线程数的关系

前面说过,目前主流 CPU 都是多核的,线程是 CPU 调度的最小单位。同一 时刻,一个 CPU 核心只能运行一个线程,也就是 CPU 内核和同时运行的线程数 是 1:1 的关系,也就是说 8 核 CPU 同时可以执行 8 个线程的代码。但 Intel 引入 超线程技术后,产生了逻辑处理器的概念,使核心数与线程数形成 1:2 的关系。 在我们前面的 Windows 任务管理器贴图就能看出来,内核数是 6 而逻辑处理器 数是 12。

在 Java 中提供了 Runtime.getRuntime().availableProcessors(),可以让我们获 取当前的 CPU 核心数,注意这个核心数指的是逻辑处理器数。

获得当前的 CPU 核心数在并发编程中很重要,并发编程下的性能优化往往 和 CPU 核心数密切相关。

上下文切换(Context switch)

既然操作系统要在多个进程(线程)之间进行调度,而每个线程在使用 CPU 时总是要使用 CPU 中的资源,比如 CPU 寄存器和程序计数器。这就意味着,操 作系统要保证线程在调度前后的正常执行,所以,操作系统中就有上下文切换的 概念,它是指 CPU(中央处理单元)从一个进程或线程到另一个进程或线程的切换。

上下文是 CPU 寄存器和程序计数器在任何时间点的内容。

寄存器是 CPU 内部的一小部分非常快的内存(相对于 CPU 内部的缓存和 CPU 外部较慢的 RAM 主内存),它通过提供对常用值的快速访问来加快计算机程序的 执行。

程序计数器是一种专门的寄存器,它指示 CPU 在其指令序列中的位置,并 保存着正在执行的指令的地址或下一条要执行的指令的地址,这取决于具体的系 统。

上下文切换可以更详细地描述为内核(即操作系统的核心)对 CPU 上的进程 (包括线程)执行以下活动:

1. 暂停一个进程的处理,并将该进程的 CPU 状态(即上下文)存储在内存中的 某个地方

2. 从内存中获取下一个进程的上下文,并在 CPU 的寄存器中恢复它

3. 返回到程序计数器指示的位置(即返回到进程被中断的代码行)以恢复进 程。

从数据来说,以程序员的角度来看, 是方法调用过程中的各种局部的变量 与资源; 以线程的角度来看, 是方法的调用栈中存储的各类信息。

引发上下文切换的原因一般包括:线程、进程切换、系统调用等等。上下文 切换通常是计算密集型的,因为涉及一系列数据在各种寄存器、 缓存中的来回 拷贝。就 CPU 时间而言,一次上下文切换大概需要 5000~20000 个时钟周期,相 对一个简单指令几个乃至十几个左右的执行时钟周期,可以看出这个成本的巨大。

Thread 和 Runnable 的区别

Thread 才是 Java 里对线程的唯一抽象,Runnable 只是对任务(业务逻辑)

的抽象。Thread 可以接受任意一个 Runnable 的实例并执行。

Callable、Future 和 FutureTask

Runnable 是一个接口,在它里面只声明了一个 run()方法,由于 run()方法返

回值为 void 类型,所以在执行完任务之后无法返回任何结果。

Callable 位于 java.util.concurrent 包下,它也是一个接口,在它里面也只声明 了一个方法,只不过这个方法叫做 call(),这是一个泛型接口,call()函数返回的 类型就是传递进来的 V 类型。

Future 就是对于具体的 Runnable 或者 Callable 任务的执行结果进行取消、查 询是否完成、获取结果。必要时可以通过 get 方法获取执行结果,该方法会阻塞 直到任务返回结果。

因为 Future 只是一个接口,所以是无法直接用来创建对象使用的,因此就 有了下面的 FutureTask。

FutureTask 类实现了 RunnableFuture 接口,RunnableFuture 继承了 Runnable 接口和 Future 接口,而 FutureTask 实现了 RunnableFuture 接口。所以它既可以 作为 Runnable 被线程执行,又可以作为 Future 得到 Callable 的返回值。

因此我们通过一个线程运行 Callable,但是 Thread 不支持构造方法中传递 Callable 的实例,所以我们需要通过 FutureTask 把一个 Callable 包装成 Runnable, 然后再通过这个 FutureTask 拿到 Callable 运行后的返回值。

要 new 一个 FutureTask 的实例,有两种方法

面试题:新启线程有几种方式?

这个问题的答案其实众说纷纭,有 2 种,3 种,4 种等等答案,建议比较好 的回答是:

按照 Java 源码中 Thread 上的注释:

官方说法是在 Java 中有两种方式创建一个线程用以执行,一种是派生自 Thread 类,另一种是实现 Runnable 接口。

当然本质上 Java 中实现线程只有一种方式,都是通过 new Thread()创建线程 对象,调用 Thread#start 启动线程。

至于基于 callable 接口的方式,因为最终是要把实现了 callable 接口的对象 通过 FutureTask 包装成 Runnable,再交给 Thread 去执行,所以这个其实可以和 实现 Runnable 接口看成同一类。

而线程池的方式,本质上是池化技术,是资源的复用,和新启线程没什么关系。 所以,比较赞同官方的说法,有两种方式创建一个线程用以执行。

中止

线程自然终止

要么是run执行完成了,要么是抛出了一个未处理的异常导致线程提前结束。 stop

暂停、恢复和停止操作对应在线程 Thread 的 API 就是 suspend()、resume()
和 stop()。但是这些 API 是过期的,也就是不建议使用的。不建议使用的原因主 要有:以 suspend()方法为例,在调用后,线程不会释放已经占有的资源(比如 锁),而是占有着资源进入睡眠状态,这样容易引发死锁问题。同样,stop()方 法在终结一个线程时不会保证线程的资源正常释放,通常是没有给予线程完成资 源释放工作的机会,因此会导致程序可能工作在不确定状态下。正因为 suspend()、 resume()和 stop()方法带来的副作用,这些方法才被标注为不建议使用的过期方 法。

中断

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

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

如果一个线程处于了阻塞状态(如线程调用了 thread.sleep、thread.join、 thread.wait 等),则在线程在检查中断标示时如果发现中断标示为 true,则会在 这些阻塞方法调用处抛出 InterruptedException 异常,并且在抛出异常后会立即 将线程的中断标示位清除,即重新设置为 false。

不建议自定义一个取消标志位来中止线程的运行。因为run方法里有阻塞调 用时会无法很快检测到取消标志,线程必须从阻塞调用返回后,才会检查这个取 消标志。这种情况下,使用中断会更好,因为,

一、一般的阻塞方法,如 sleep 等本身就支持中断的检查, 二、检查中断位的状态和检查取消标志位没什么区别,用中断位的状态还可

以避免声明取消标志位,减少资源的消耗。

注意:处于死锁状态的线程无法被中断

深入理解 run()和 start()

Thread类是Java里对线程概念的抽象,可以这样理解:我们通过new Thread() 其实只是 new 出一个 Thread 的实例,还没有操作系统中真正的线程挂起钩来。 只有执行了 start()方法后,才实现了真正意义上的启动线程。

从 Thread 的源码可以看到,Thread 的 start 方法中调用了 start0()方法,而 start0()是个 native 方法,这就说明 Thread#start 一定和操作系统是密切相关的。

start()方法让一个线程进入就绪队列等待分配 cpu,分到 cpu 后才调用实现 的 run()方法,start()方法不能重复调用,如果重复调用会抛出异常(注意,此处 可能有面试题:多次调用一个线程的 start 方法会怎么样?)。

而run方法是业务逻辑实现的地方,本质上和任意一个类的任意一个成员方 法并没有任何区别,可以重复执行,也可以被单独调用。

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

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

相关文章

【算法练习Day42】买卖股票的最佳时机 III买卖股票的最佳时机 IV

​📝个人主页:Sherry的成长之路 🏠学习社区:Sherry的成长之路(个人社区) 📖专栏链接:练题 🎯长路漫漫浩浩,万事皆有期待 文章目录 买卖股票的最佳时机 III买卖…

超声波热量表和电磁热量表有哪些区别?

随着我们能源消耗日益增长,热量计量已成为节能减排、能源管理的重要手段。热量表是用于测量热能消耗的仪表,其中超声波热量表和电磁热量表是常见的两种类型。下面,就由小编来为大家详细的介绍下超声波热量表和电磁热量表的区别,一…

【案例卡】clickhouse:多行数据拼接在一行

一、需求 针对clickhouse数据库中,group by 分组后的字符串字段,拼接处理在一行的问题实现。在mysql中,可以用group_concat()函数来实现,而clickhouse数据库不支持此函数,特此记录实现方式。 二、clickhouse相关函数…

桌面远程连接

遇到的问题: win11家庭版不支持远程连接,如下图所示: 解决办法: 被控方电脑 1、打开控制面板-系统和安全-允许远程访问,勾选允许远程协助连接这台计算机 2、打开控制面板-系统和安全-Windows Defender 防火墙-允许…

php加密解密的用法(对称加密,非对称加密)

加密和摘要的区别 ***摘要:是从已知的数据中,通过摘要计算出一个值,一个数据对应一个或多个摘要的值 *** 比如:md5 和 sha1 sha256 hash 就是得到一个特定的值 ,同一个数据得到的md5 是一样的,不会改变的 比…

【C++】万字详解IO流(输入输出流+文件流+字符串流)

文章目录 一、标准输入输出流1.1提取符>>&#xff08;赋值给&#xff09;与插入符<<&#xff08;输出到&#xff09;理解cin >> a理解ifstream&#xff08;读&#xff09; >> a例子 1.2get系列函数get与getline函数细小但又重要的区别 1.3获取状态信息…

动态表单获取某一项值

<template><div><el-form :model"form" :rules"rules" ref"form"><el-row v-for"(item,index) in form.list" :key"index"><el-col :span"6"><el-form-item label"用户名称…

超好用!在线即可制作电子产品图册

​电子产品图册是展示产品特点、功能和外观的重要方式之一。通过图册&#xff0c;可以让客户更好地了解产品&#xff0c;增强信任感&#xff0c;从而促进销售。同时&#xff0c;对于企业来说&#xff0c;制作精美的电子产品图册也是展示企业文化和品牌形象的重要手段之一。 一、…

Nacos服务注册

Nacos: 注册中心 配置中心 服务注册/发现&#xff1a;Nacos Discovery Nacos Discovery与Netflix Ribbon集成&#xff0c;RestTemplate或OpenFeign可用于服务到服务的调用 Nacos 配置规则 Nacos命名空间、分组、DataId关系 Nacos集群架构 三种部署模式 集群部署 默认Na…

Git的安装和常用命令Git与SVN的区别Gitee远程仓库团队开发代码共享演示

目录 一、Git入门 1.1 Git简介 1.2 Git与SVN的区别 1.2.1 详解 1.2.2 图解 1.3 Git相较于SVN的优势与劣势 1.3.1 Git的优势与劣势 1.3.2 SVN的优势与劣势 1.4 Git的工作流程 1.4.1 图解 1.4.2 详解 二、Git的安装以及常用命令 2.1 Git官网链接 2.2 安装步骤 2.…

AppWeb 身份验证绕过漏洞 (CVE-2018-8715)漏洞复现

漏洞描述 AppWeb 是一个嵌入式 Web 服务器&#xff0c;基于由 Embedthis Software LLC 开发和维护的开源 GPL 协议。它是用C / C编写的&#xff0c;几乎可以在任何现代操作系统上运行。当然&#xff0c;它旨在为嵌入式设备提供一个 Web 应用程序容器。 AppWeb 可以配置为身份…

智能井盖生产商家,万宾科技井盖传感器产品详情

市政府管理水平决定城市人民幸福程度&#xff0c;所以在智慧城市推进过程中&#xff0c;市政府也在加快城市信息基础设施建设&#xff0c;希望提高公共服务水平&#xff0c;以此来满足城市居民的需求&#xff0c;进一步推进城市信息化智能化发展。作为城市生命线的一个组成部分…

Java线程状态转换

从java层面&#xff0c;线程状态分为六种&#xff0c;分别是New、Blocked、Waiting、Timed_Waiting、Terminated和Runnable New&#xff1a;初始状态&#xff0c;线程刚刚创建还未调用start方法&#xff0c;线程还没有和操作系统的线程关联起来 New->Runnable(箭头1)&#…

MySQL 批量修改表的列名为小写

1、获取脚本 SELECT concat( alter table , TABLE_NAME, change column , COLUMN_NAME, , lower( COLUMN_NAME ), , COLUMN_TYPE, comment \, COLUMN_COMMENT, \; ) AS 脚本 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA 数据库名 and TABLE_NAME表名-- 大写是up…

ArcGIS小技巧|四种计算图斑面积的方法

ArcGIS中有多种方法可计算出图斑面积&#xff0c;本文总结了四种方法&#xff0c;是否可堪称史上最全&#xff1f; 1、计算几何 这是最适合非专业人士的方法&#xff0c;直接利用ArcGIS中的计算几何功能进行计算。 a、首先添加一double类型字段&#xff0c;用来存储面积数值。…

中文编程软件视频推荐,自学编程电脑推荐,中文编程开发语言工具下载

中文编程软件视频推荐&#xff0c;自学编程电脑推荐&#xff0c;中文编程开发语言工具下载 给大家分享一款中文编程工具&#xff0c;零基础轻松学编程&#xff0c;不需英语基础&#xff0c;编程工具可下载。 这款工具不但可以连接部分硬件&#xff0c;而且可以开发大型的软件…

常用设计模式——策略模式

策略模式是什么 策略模式&#xff08;Strategy&#xff09;&#xff1a;针对一组算法&#xff0c;将每一个算法封装起来&#xff0c;从而使得它们可以相互替换。 比如我们一个软件的会员等级&#xff0c;每一个等级都会有对应的一些等级权益&#xff0c;那么每一个等级权益就…

Maven中的继承与聚合

一&#xff0c;继承 前面我们将项目拆分成各个小模块&#xff0c;但是每个小模块中有很多相同的依赖于是我们创建一个父工程将模块中相同的依赖定义在父工程中&#xff0c;然后子工程继承父工程Maven作用&#xff1a;简化依赖配置&#xff0c;统一依赖管理,可以实现多重继承像J…

MySQL的存储过程

存储过程&#xff1a;是一组为了完成特定功能的sql语句的集合&#xff0c;类似于函数 写好一个存储过程之后&#xff0c;我们可以像函数一样随时可以调用sql的集合 复杂的&#xff0c;需要很多sql语句联合执行完成的任务 存储过程在执行上比sql语句执行速度快&#xff0c;效率…

财报解读:抢滩“睡眠经济”,麒盛科技如何制胜市场?

现代市场经济理论的鼻祖亚当斯密曾说&#xff0c;有需求就有市场&#xff0c;有市场才有发展。 调查研究显示&#xff0c;我国超3亿人存在睡眠障碍&#xff0c;其中超3/4的人晚11点以后入睡&#xff0c;近1/3的人熬到凌晨1点以后才能入睡。针对“睡个好觉”需求的“睡眠经济”…