第1章 多线程基础

news2025/1/25 4:28:50

第1章 多线程基础

1.1.2 线程与进程的关系

进程可以看成是线程的容器,而线程又可以看成是进程中的执行路径。

1.2 多线程启动

线程有两种启动方式:实现Runnable接口;继承Thread类并重写run()方法。

执行进程中的任务时才会产生线程,因此需要一种描述任务的方式,这可以由Runnable接口来提供。要想定义任务,需要实现Runnable接口并且重写run()方法,然后再将Runnable的实现对象作为参数传递给Thread类。

调用Thread类的start()方法,启动线程,向CPU发出请求,去执行任务。

还可以采用继承Thread类并且重写run()方法,然后调用start()启动线程。

通常情况下,实现Runnable接口然后启动线程是一个更好的选择,这可以提高程序的灵活性和扩展性,并且用Runnable接口描述任务也更容易理解。

1.2.1 线程标识

Thread类用于管理线程,如设置线程优先级、设置Daemon属性、读取线程名字和ID、启动线程任务、暂停线程任务、中断线程等。

为了管理线程,每个线程在启动后都会生成一个唯一的标识符,并且在其生命周期内保持不变当线程被终止时,该线程ID可以被重用。而线程的名字更加直观,但是不具有唯一性。

1.2.2 Thread与Runnable

Runnable接口表示线程要执行的任务。当Runnable中的run()方法执行时,表示线程在激活状态,run()方法一旦执行完毕,即表示任务完成,则线程将被停止。

Thread类默认实现了Runnable接口,并且其构造方法的重载形式允许传入Runnable接口对象作为任务。

通过Thread类的源代码可以发现,线程的两种启动方式,其本质都是实现Thread类中的run()方法而实现Runnable接口,然后传递给Thread类的方式,比Thread子类重写run()方法更加灵活。

1.2.3 run()与start()

调用Thread对象的start()方法,使线程对象开始执行任务,这会触发Java虚拟机调用当前线程对象的run()方法。调用start()方法后,将导致两个线程并发运行,一个是调用start()方法的当前线程,另外一个是执行run()方法的线程。

如果重复调用start()方法,这是一个非法操作,它不会产生更多的线程,反而会导致IllegalThreadStateException异常。

1.2.4 Thread源码分析

创建Thread类实例,首先会执行registerNatives()方法,它在静态代码块中加载。线程的启动、运行、生命期管理和调度等都高度依赖于操作系统,Java本身并不具备与底层操作系统交互的能力。因此线程的底层操作都使用了native方法,registerNatives()就是用C语言编写的底层线程注册方法

无论通过Thread类的哪种构造方法去创建线程,都需要首先调用init()方法,初始化线程环境

在init()方法中,做了如下操作:

(1)设置线程名称。

(2)将新线程的父线程设置为当前线程。

(3)获取系统的安全管理SecurityManager,并获得线程组。SecurityManager在Java中被用来检查应用程序是否能访问一些受限资源,如文件、套接字(socket)等。它可以用在那些具有高安全性要求的应用程序中。

(4)获取线程组的权限检查。

(5)在线程组中增加未启动的线程数量。

(6)设置新线程的属性,包括守护线程属性(默认继承父线程)、优先级(默认继承父线程)、堆栈大小(如果为0,则默认由JVM分配)、线程组、线程安全控制上下文(一种Java安全模式,设置访问控制权限)等。

1.3 线程状态

Java中的线程存在6种状态,分别是NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED。我们可以通过Thread类中的Thread.getState()方法获取线程在某个时期的线程状态。在给定的时间点,线程只能处于一种状态。

1.3.1 NEW状态

NEW代表着线程新建状态,一个已创建但是未起动(start)的线程处于NEW状态。

1.3.2 RUNNABLE状态

RUNNABLE状态表示一个线程正在Java虚拟机中运行,但是这个线程是否获得了处理器分配资源并不确定。调用Thread的start()方法后,线程从NEW状态切换到了RUNNABLE状态。

1.3.3 BLOCKED状态

BLOCKED为阻塞状态,表示当前线程正在阻塞等待获得监视器锁。当一个线程要访问被其他线程synchronized锁定的资源时,当前线程需要阻塞等待。

1.3.4 WAITING状态

WAITING表示线程处于等待状态。

在当前线程中调用如下方法之一时,会使当前线程进入等待状态:

Object类的wait()方法(没有超时设置);

Thread类的join()方法(没有超时设置);

LockSupport类的park()方法。

处于等待状态的线程,正在等待另外一个线程去完成某个特殊操作。例如,在某个线程中调用了Object对象的wait()方法,它会进入等待状态,等待Object对象调用notify()或notifyAll()方法。一个线程对象调用了join()方法,则会等待指定的线程终止任务。

1.3.5 TIMED_WAITING状态

TIMED_WAITING表示线程处于定时等待状态。

在当前线程中调用如下方法之一时,使当前线程进入定时等待状态:

Object类的wait()方法(有超时设置);

Thread类的join()方法(有超时设置);

Thread类的sleep()方法(有超时设置);

LockSupport类的parkNanos ()方法;

LockSupport类的parkUntil()方法。

1.3.6 WAITING与BLOCKED的区别

WAITING、TIMED_WAITING、BLOCKED这几个线程状态,都会使当前线程处于停顿状态,因此容易混淆。下面简单总结一下这些状态之间的区别:

(1)Thread.sleep()不会释放占有的对象锁,因此会持续占用CPU。

(2)Object.wait()会释放占有的对象锁,不会占用CPU。

(3)BLOCKED使当前线程进入阻塞后,为了抢占对象监视器锁,一般操作系统都会给这个线程持续的CPU使用权。

(4)LockSupport.park()底层调用UNSAFE.park()方法实现,它没有使用对象监视器锁,不会占用CPU。

1.3.7 TERMINATED状态

TERMINATED表示线程为完结状态。当线程完成其run()方法中的任务,或者因为某些未知的异常而强制中断时,线程状态变为TERMINATED。

1.3.8 线程状态转换

前面我们学习了Java线程的6种状态,接下来通过图1-2对线程状态转换进行汇总。在图1-2中,把线程RUNNABLE状态细分为两种:runnable(准备就绪)和running(运行中)。runnable表示线程刚刚被JVM启动,还没有获得CPU的使用权;running表示线程获得了CPU的使用权,正在运行。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MyGQFkvR-1678263771531)(null)]

1.4 sleep()与yield()

1.4.1 线程休眠sleep()

Thread类的sleep()方法,使当前正在执行的线程以指定的毫秒数暂时停止执行,具体停止时间取决于系统定时器和调度程序的精度和准确性。当前线程状态由RUNNABLE切换到TIMED_WAITING。调用sleep()方法不会使线程丢失任何监视器所有权,因此当前线程仍然占用CPU分片。

调用sleep()方法可能会抛出InterruptedException异常,它应该在run()方法中被捕获,因为异常无法传递到其他线程,如主线程就无法捕获子线程抛出的异常。

Java SE5引入了更加显式的sleep()版本,作为TimeUit类的一部分。例如,TimeUnit.MILLISECONDS.sleep(1000)等价于Thread.sleep(1000),表示休眠1秒。TimeUnit类提供了更好的可读性。

1.4.2 线程让步yield()

Thread类的yield()方法对线程调度器发出一个暗示,即当前线程愿意让出正在使用的处理器。调度程序可以响应暗示请求,也可以自由地忽略这个提示。如图1-2所示,线程调用yield()方法后,可能从running状态转为runnable状态。

需要强调的是:**yield()仅仅是一个暗示,没有任何机制保证它一定会被采纳。**线程调度器是Java线程机制的底层对象,可以把CPU的使用权从一个线程转移到另外一个线程。如果你的计算机是多核处理器,那么分配线程到不同的处理器执行任务要依赖线程调度器。

1.5 线程优先级

每个线程都有优先级。具有较高优先级的线程可能优先获得CPU的使用权。创建一个新的Thread对象时,新线程的优先级默认与创建线程的优先级一致。

JDK中实际上存在着10个优先级,但是这与大多数操作系统不能建立很好的映射关系。比如Windows有7个线程优先级设置,而Sun的Solaris只有两个线程优先级,因此在Java中一般只使用下面的三种优先级设置。

image-20230306170335381

不应该过分依赖于线程优先级的设置,理论上线程优先级高的会优先执行,但实际情况可能并不明确。例如,线程调度机制还没有来得及介入时,线程可能就已经执行完了。所以优先级具有一定的“随机性”。

1.5.1 线程优先级与资源竞争

具有较高优先级的线程会优先得到调度系统资源分配。也就是说优先级高的线程可以优先竞争共享资源。但线程的优先级调度和底层操作系统有密切的关系,在各个平台上表现不一并且无法精准控制。因此在要求严格的场合,需要开发者在应用层解决线程调度问题。

当调用Thread.yield()方法时,会给线程调度器一个暗示,即优先级高的其他线程或相同优先级的其他线程,都可以优先获得CPU分片。

1.6 守护线程

1.6.1 守护线程的概念

在Java线程中有两种线程,一种是用户线程,另一种是守护线程(Daemon)。

所谓守护线程,是指在程序运行的时候在后台提供一种通用服务的线程。比如,垃圾回收线程就是一个很称职的守护者(当一个对象不再被引用的时候,内存回收它占领的空间,以便空间被后来的新对象使用)。

Daemon线程与用户线程在使用时没有任何区别,唯一的不同是:当所有用户线程结束时,程序也会终止,Java虚拟机不管是否存在守护线程,都会退出。

调用Thread对象的setDaemon()方法,可以把用户线程标记为守护者。调用isDaemon()方法可以判断线程是否是一个守护线程。

空间,以便空间被后来的新对象使用)。

Daemon线程与用户线程在使用时没有任何区别,唯一的不同是:当所有用户线程结束时,程序也会终止,Java虚拟机不管是否存在守护线程,都会退出。

调用Thread对象的setDaemon()方法,可以把用户线程标记为守护者。调用isDaemon()方法可以判断线程是否是一个守护线程。

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

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

相关文章

用Python写一个zidong发送直播弹幕脚本,让你在直播间霸屏

前言 现在直播这个东西可以说是哪哪都有,有些的直播还有看弹幕来进行抽奖活动,这不我表弟昨天晚上就碰上一个了 说是主播随机抽取一个弹幕抽奖,我表弟辛辛苦苦手动发送了一晚上的弹幕,结果啥也没中,怪可怜的 今天闲…

65 - 进程互斥锁的优化实现

---- 整理自狄泰软件唐佐林老师课程 文章目录1. 问题一1.1 当前实现的深入分析1.2 解决方案设计1.3 解决方案实现步骤1.4 编程实验:EnterCritical()重设计2. 问题二2.1 互斥锁的优化设计2.2 互斥锁的优化实现方案2.3 编程实验:互斥锁的优化实现3. 小结1.…

【C语言】每日刷题 —— 牛客

前言 大家好,今天带来一篇新的专栏 c_牛客,不出意外的话每天更新十道题,难度也是从易到难,自己复习的同时也希望能帮助到大家,题目答案会根据我所学到的知识提供最优解。 🏡个人主页:悲伤的猪大…

小白做什么兼职项目赚钱?宝妈拍短视频赚钱的方法

很多宝妈在家带孩子之余想做兼职赚点小钱,依靠互联网无疑是比较方便的途径,在刷单、微商等网上兼职成为过去式以后,很多宝妈选择了短视频创业。 宝妈怎么拍短视频? 宝妈因为要照顾宝宝还要兼顾家务,空闲的时间比较琐碎…

Rust Web入门(七):WebAssembly

本教程笔记来自 杨旭老师的 rust web 全栈教程,链接如下: https://www.bilibili.com/video/BV1RP4y1G7KF?p1&vd_source8595fbbf160cc11a0cc07cadacf22951 学习 Rust Web 需要学习 rust 的前置知识可以学习杨旭老师的另一门教程 https://www.bili…

5款电商团队必须会用的任务管理工具

随着企业数字化转型,高效率的团队协作和远程协同办公成为越来越多团队的强烈需求,今天说说电商团队,电商运营很大一个特点是多场景、跨部门、高频协作。并且要通过多平台和形式跟新兴媒体进行品宣争夺市场份额。 这种情形对跨部门的团队协作…

java基础学习 day50(内部类)

什么是内部类? 写在一个类里面的类就叫做内部类 什么时候用到内部类? B类表示的事物是A类的一部分,且B单独存在没有意义。 比如:汽车和发动机,ArrayList和迭代器,人和心脏 内部类的访问特点 内部类可以…

STM32U5开发(1)----通过 USART1 发送数据

概述 通过 USART1 发送一些数据。 最近在弄ST和GD的课程,需要样片的可以加群申请:6_15061293。 生成例程 使用STM32CUBEMX生成例程,这里使用NUCLEO-U575ZI开发板。 选择工程的时候,先不必选择加载了TrustZone。 样品申请 h…

STM32程序下载和启动方式

目录1 BOOT引脚配置和下载说明2 关于串口下载方式3 关于一按复位就跑代码4 关于下载调试速度5 关于三种启动方式5.1 FLASH启动5.2 系统存储器器启动5.3 SRAM启动6 关于程序的三种下载方式1 BOOT引脚配置和下载说明 BOOT0BOOT1程序运行ST-Link下载串口下载启动说明xx无0x√√用…

AList搭建网盘挂载硬盘并挂载网络资源(傻瓜式自配置教程)

AList搭建网盘挂载硬盘并挂载网络资源1.安装AList1.1 下载1.2 解压1.3 启动1.4 登录1.5 改密1.6 开机自启2.添加云盘存储2.1 添加阿里云盘2.2 阿里云刷新令牌2.3 查看云盘文件3.映射本地盘3.1 下载RaiDrive3.2 安装3.2 设置4.延伸参考资料: AList: https://alist.n…

Warshall算法

🚀write in front🚀 📜所属专栏:> 算法 🛰️博客主页:睿睿的博客主页 🛰️代码仓库:🎉VS2022_C语言仓库 🎡您的点赞、关注、收藏、评论,是对我…

数据中台架构体系理解

目前,大部分企业更倾向于数据集中采集、存储,并应用分层建设。这种方式一方面有利于应用系统的快速部署,另一方面也保证了数据的集中管理与运营,体现数据的资产、资源属性。 数据中台的出现弥补了数据开发和应用开发之间由于开发…

工厂设计模式

介绍 Java工厂设计模式主要分为三种: 简单工厂模式(Simple Factory Pattern):使用一个工厂类来封装对象创建的过程,客户端只需要通过传递不同的参数来获取不同的产品对象,从而避免了客户端直接创建产品对象的操作工厂方法模式(Factory Method Pattern):将工厂类抽象出来,每个…

fastjson 返回 $ref 数据

文章目录问题描述:1、重复引用:2、循环引用:原因分析:1、重复引用:2、循环引用:反序列化:1、开启引用检测:2、关闭引用检测:小结:问题描述: 问题…

小樽C++ 单章⑨ 文件

目录 1.文件类型变量的定义与引用 1.1 文件的读写 1.2 fopen()版 (C专用) 1.3 文件输入输出流 (C专用) 文件有两种保存方式:二进制文件、文本文件。例如存121这个数字。 二进制存储效率高,但是对我们不友好,对每个值都要变成二进制太难啦…

uniapp实现自定义相机

自定义相机起因由于最近用uniapp调用原生相机容易出现闪退问题,找了很多教程又是压缩图片又是优化代码,我表示并没有太大作用!!实现自定义相机使用效果图拓展实现多种自定义相机水印相机身份证相机人像相机起因 由于最近用uniapp调用原生相机容易出现闪退…

MindAR的网页端WebAR图片识别功能的图片目标编译器中文离线版本功能(含源码)

前言 之前制作了基于MindAR实现的网页端WebAR图片识别叠加动作模型追踪功能的demo,使用了在线的图像目标编译器对识别图进行了编译,并实现了自制的WebAR效果,大致效果如下: 但是在线的编译器在操作中也不是很方便,我…

207. 课程表

207. 课程表https://leetcode.cn/problems/course-schedule/ 难度中等1526 你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。 在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] [a…

One-YOLOv5 v1.2.0 Released(支持分类,检测,实例分割)

0x0. 引言0x1. 快速开始0x2. 在COCO上的精度表现 yolov5s-defaultyolov5s-seg 0x3. 在COCO上的单GPU性能表现特性 & bug 修复 特性用户反馈的bug 下个版本的展望附件常用预训练模型下载列表 0x0. 引言 🌟 v1.2.0同步了ultralytics yolov5的上游分支v7.0 &…

Python+ChatGPT制作一个AI实用百宝箱

目录一、注册OpenAI二、搭建网站及其框架三、AI聊天机器人四、AI绘画机器人ChatGPT 最近在互联网掀起了一阵热潮,其高度智能化的功能能够给我们现实生活带来诸多的便利,可以帮助你写文章、写报告、写周报、做表格、做策划甚至还会写代码。只要与文字相关…