【Java系列】详解多线程(二)——Thread类及常见方法(上篇)

个人主页:兜里有颗棉花糖
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创
收录于专栏【Java系列专栏】【JaveEE学习专栏】
本专栏旨在分享学习Java的一点学习心得,欢迎大家在评论区交流讨论💌

目录

  • 一、前文回顾
  • 二、创建线程的几种方式。
    • 继承Thread类
    • 实现runnable方法
    • lambda表达式
  • 三、Thread类及常见方法
    • Thread类的常见构造方法
    • Thread类的属性

一、前文回顾

我们先来回顾一下线程与进程之间的联系。

我们知道多进程可以帮助我们完成并发编程,即可以把多个cpu核心充分利用起来以完成同时执行多任务的场景。但是进程有一个问题就是进程的创建和销毁的开销是比较大的,如果我们需要频繁的创建和销毁进程的话,那么多进程这种方式就是比较低效的。所以就衍生出了轻量级进程——线程。线程之所以比进程更轻量级主要有两方面的原因

  • 一方面是线程共享进程的资源,不要忘记,线程是进程的一部分,线程可以共享那些属于进程的资源,但是进程之间是相对独立的,线程创建时不需要像进程那样需要分配独立的资源,因此线程创建和销毁的开销会比进程小很多
  • 另一方面线程调度切换要比进程调度切换要快上很多,由于多线程使用的调度算法比单进程要复杂很多,加上线程对系统资源的共享(线程在同一个进程内共享资源,线程切换只需要切换线程的上下文,而不需要切换整个进程的上下文;而相比之间进程切换需要保存和恢复进程的所有上下文信息,包括内存映像、打开的文件、进程状态等。),所以线程调度之间的切换要比进程调度的切换快上很多,也更容易实现多任务的处理。

线程在同一进程内共享内存资源,所以当进程创建完成时,线程已经可以访问和共享这些内存资源(主线程是在进程创建时默认创建的一个线程)。如果后续需要我们创建其它的线程,我们就可以重用之前的字眼就可以了。

进程包含了线程,一个进程内部至少有一个线程,进程是系统中分配资源的基本单位,而线程是cpu调度执行的基本单位。我们可以这样说线程的引入最主要的就是为了实现更高效的并发处理和资源共享。

由于线程可以共享同一进程内的内存资源,因此资源分配的主要任务是为进程分配足够的内存空间,对线程的资源分配比在进程级别上的资源分配要少得多。

现在问题来了,一个进程内既然可以存在多个线程,同时线程可以共享同一个进程内的资源,这也意味着这多个线程之间的相互干扰会比较大(这一点就不如进程之间那样相对独立一些,一个进程挂掉知道后不会对其它进程产生影响。),一旦某个出现异常之后就有可能导致整个进程都会挂掉,这当然会对其它线程产生影响。另外,多个线程去访问同一块公共资源的时候也可能会出现冲突带来线程安全的问题。

所以,总的来说使用多线程的方式完成任务比多进程的方式完成任务会有优势,但同时存在一些缺点。但尽管如此,我们依然可以利用多线程的方式来很好的完成并发编程的任务。

好了,友友们,回顾到此结束,接下来我们一起学习新的内容吧!!!

二、创建线程的几种方式。

Java标准库中,提供了Thread()类来表示线程,同时Java创建线程的方式有很多种。最常见的两种写法:继承Thread()类重写Runnable方法()

继承Thread类

方式一:通过继承thread类来重写run方法是一种创建线程的方式(这里我们需要自己创建一个类来继承thread方法);方式二:还有一种方式我们也可以创建线程:基于匿名内部类的形式来继承thread类,并重run方法。

现在我们通过匿名内部类的方式来创建线程(基于匿名内部类的形式来继承thread类,并重写run方法,即方式二):

代码如下:

public class Demo03 {public static void main(String[] args) {Thread t = new Thread() {@Overridepublic void run() {while(true) {System.out.println("hello thread!!!");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}};t.start();}
}

解释:上述代码中我们创建了一个子类,这个子类(此类就是我们说的匿名内部类)继承自Thread类,同时我们在这个子类重对run方法进行了重写。
另外我们还创建了该子类(也就是之前说的匿名内部类)的实例,用引用t类指向这个实例。
最后就是通过引用t来调用start方法来调用系统API,再从内核中把线程创建出来。
在这里插入图片描述
最后我们来看一下运行结果:
在这里插入图片描述

实现runnable方法

方式三通过实现runnable来重写run方法(这里是自己创建一个类)的方式可以创建一个线程。方式四:另外我们这里依然可以用其它方式来创建一个线程:即基于匿名内部类的形式来实现runnable并重写run方法。

现在我们通过匿名内部类的形式来实现runnable并重写run方法(即方式四)来创建线程,代码如下:

public class Demo04 {public static void main(String[] args) {Thread t = new Thread(new Runnable() {@Overridepublic void run() {while(true) {System.out.println("hello world");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}});t.start();}
}

解释,上述代码中,我们创建了runnable的子类,并重写了run方法,然后创建出了runnable的子类的实例,把这个实例传给Thread的构造方法。

lambda表达式

我们还可以通过lambda表达式来表示run方法的内容,从而创建出线程。

代码如下:

public class Demo05 {public static void main(String[] args) {Thread t = new Thread(() -> {while(true) {System.out.println("hello world");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.start();}
}

什么是lambda表达式:lambda表达式本质上就是一个匿名函数,主要可以用来作为回调函数来进行使用。
先来回顾一下回调函数吧:回调函数不需要我们自己去进行调用,因为回调函数会在特定的时机自动地被调用。
此时的回调函数就是在线程创建成功之后才会真正执行。

关于线程的创建这里,也有其它的方式,比如基于callable的方式创建线程,基于线程池的方式创建线程。

三、Thread类及常见方法

Thread类是JVM用来管理线程的一个类,换句话说,每个线程都有一个唯一的Thread对象与之关联。我们对线程的各种操作都是根据Thread类进行展开的。

Thread类的常见构造方法

这里我们可以参照Java官方文档:https://docs.oracle.com/javase/8/docs/api/index.html
在这里插入图片描述

在这里插入图片描述

如下图的代码中,我们给线程起了个名字mythread
在这里插入图片描述
我们可以通过JVM中的jconsole.exe来看到这个线程(mythread):
在这里插入图片描述
现在问题来了,为什么我们没有看到主线程(main)呢?在上述代码中,主线程创建并启动了一个新线程,而新线程负责打印"hello thread"的消息。由于新线程的循环中有一个线程休眠的操作(Thread.sleep(1000)),所以您将会在不同时间间隔内看到"hello thread"的输出。但这个输出是由新线程负责的,而不是主线程。
所以,我们没有看到主线程的原因是主线程没有显示任何输出或执行其他代码来表明它的存在。它仅仅完成了创建和启动新线程的任务,然后退出。
对于主线程来说,main方法就是主线程的入口;而对于其它线程来说,lambda或者run就是线程的入口。所以执行完线程的入口函数,线程就算是结束了。

Thread类的属性

下面是Thread类的几个常见属性:

属性获取方法
IDgetId()
名称getName()
状态getState()
优先级getPriority()
是否后台线程isDasmon()
是否存活isAlive()
是否被中断isInterrupted()
  • ID:这里是JVM给线程设定的身份标识。对于一个线程来说,身份标识可以有好多个,就像一个人有大名也有小名。比如JVM给某一线程设定了一个身份标识,phread库(系统给开发人员提供的操作线程的API)也有一个线程的身份标识,内核中还有一个线程的身份标识,这几个身份标识之间相互独立互不干扰。
  • 名称:设置线程名称方便我们知道这个线程是干什么用的,同时也方便我们去进行调试。
  • 状态:Java中的线程状态和操作系统中的有所区别,Java中的线程状态更加细化。
  • 优先级:关于获取、设置线程的优先级其实并没有太大意义,因为系统内核进行线程调度的速度极快(线程调度是由系统内核负责的),快到我们根本无法感知,所有我们一般使用默认的线程优先级即可。
  • 是否是后台线程:后台线程又称为守护线程,后台线程不会影响线程结束;而前台线程会影响线程结束,如果前台线程没有执行完的话,进程是不会结束的;一个进程中如果所有的前台线程都执行完此时进程退出,如果此时依然存在后台线程没有执行完的话,后台线程依然会随着进程的退出而退出。我们创建的线程默认是前台线程。如下代码进行演示:
// 这里我们创建的线程是前台线程
public class Demo07 {public static void main(String[] args) {Thread t = new Thread(()->{while(true) {System.out.println("hello thread!!!");}});t.start();}
}// 这里将我们创建的线程设置为了后台线程
public class Demo07 {public static void main(String[] args) {Thread t = new Thread(()->{while(true) {System.out.println("hello thread!!!");}});t.setDaemon(true);t.start();}
}

当我们把创建的线程设置为后台线程之后,程序运行起来之后就会立即结束。原因:由于我们把自己手动的线程创建成了后台线程,所以此时就只剩下main主线程了,而main线程这里不需要执行代码,所以执行时间极短main线程(前台线程)就结束了,而我们设置的后台线程还没有来得及执行就随着进程的退出而退出了。

  • 是否存活(isAlive()):这里的存活指的并不是thread对象是否存活,而是指的thread对象(我们也可以称为线程对象)对应的线程(即系统内核中的线程)是否存活。Thread对象的声明周期并不是和系统内核中的线程的生命周期完全一致。一般来说都是先把thread对象创建好,然后手动调用start方法,此时内核才真正创建出线程。对于线程,当它的run方法执行完后,线程的生命周期也会自然结束,就算线程对象还存在于内存中。此时该线程会释放占用的资源并进入死亡状态;还有另外一种情况就是线程对象生命周期的结束(即没有引用指向该线程对象了),此时线程也会结束,这种情况下,线程的run方法执行与否并不影响线程的结束状态。垃圾回收器会在适当时候回收无引用的线程对象,并释放相关资源。

好了,本文到这里就结束了,希望友友们可以支持一下一键三连哈。嗯,就到这里吧,再见啦!!!

在这里插入图片描述

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

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

相关文章

Proxmox创建CentOS虚拟机

文章目录 下载ISO安装文件上传创建虚拟机启动虚拟机设置DNS CentOS配置国内安装源备份原有安装源下载更新国内源清理yum缓存制作新配置文件缓存 下载ISO安装文件 下载地址:https://www.xitongzhijia.net/ 也可去官网进行下载 上传 下面介绍直接通过页面上传&…

【论文阅读笔记】M3Care: Learning with Missing Modalities in Multimodal Healthcare Data

本文介绍了一种名为“MCare”的模型,旨在处理多模态医疗保健数据中的缺失模态问题。这个模型是端到端的,能够补偿病人缺失模态的信息,以执行临床分析。MCare不是生成原始缺失数据,而是在潜在空间中估计缺失模态的任务相关信息&…

idea一些报错

java: 非法字符: \ufeff 使用IDEA修改文件编码 在IDEA右下角,将编码改为GBK,再转为UTF-8,重新启动项目。具体步骤如下: 在IDEA右下角找到UTF-8字样的编码格式设计项,点击选择第一项GBK,然后Convert&#xf…

JVM虚拟机系统性学习-对象的创建流程及对象的访问定位

对象的创建流程与内存分配 对象创建流程如下: Java 中新创建的对象如何分配空间呢? new 的对象先放 Eden 区(如果是大对象,直接放入老年代)当 Eden 区满了之后,程序还需要创建对象,则垃圾回收…

Linux的五种IO模型

众所周知,出于对 OS 安全性的考虑,用户进程是不能直接操作 I/O 设备的。必须通过系统调用请求操作系统内核来协助完成 I/O 动作。 下图展示了 Linux I/O 的过程。 操作系统内核收到用户进程发起的请求后,从 I/O 设备读取数据到 kernel buff…

【超详细】创建vue3+ts项目(引入ElementPlus、Axios)

目录 前言1、使用vue脚手架创建项目1.1检查vue版本1.2 使用vue脚手架创建项目 2、删除项目多余文件,修改配置项目2.1、删除以下文件2.1、在views下创建index文件2.2、修改router/index.ts路由文件:2.3、修改App.vue文件:2.4、初始化页面样式以…

windows上抓包出现大包未分片以及关闭tso方法

wireshark抓包中会有大数据包(未分片包)和ip校验和不对的包,问题根因在目前很多电脑网卡支持TSO和将校验和计算到网卡上,导致抓出数据包未分片 详细文章看: https://www.cnblogs.com/charlieroro/p/11363336.html 目前很多网卡已…

业务代码-整合框架-存储-缓存常见错误详解一

一. java空指针和异常: 1.什么是空指针异常(java.lang.NullPointException): 1.1常见的空指针异常案例: public class WhatIsNpe {public static class User {private String name;private String[] address;public void print…

项目一 分析并设计学生管理数据库

项目一 分析并设计学生管理数据库 1,做好管理数据库的知识准备 1.1,初识数据库 **1,DBMS:**数据库管理系统(Database Management System)。数据库 是通过DBMS创建和操作的 容器。 **2,DB:**数据库(data…

004 Windows NTFS文件夹权限

一、NTFS文件权限: NTFS(New Technology File System)是Windows NT内核的系列操作系统支持的、一个特别为网络和磁盘配额、文件加密等管理安全特性设计的磁盘格式,提供长文件名、数据保护和恢复,能通过目录和文件许可…

ffmpeg编解码——数据包(packet)概念(如何正确处理数据包中的显示时间戳pts与解码时间戳dts关系?)

文章目录 FFmpeg编解码——数据包(Packet)概念1. 数据包(Packet)简介2. 数据包(Packet)在FFmpeg中的应用2.1 从媒体文件读取数据包2.2 向媒体文件写入数据包 3. 数据包(Packet)相关问…

【EI会议征稿中|IEEE出版】第三届信息技术与当代体育国际学术会议(TCS 2023)

【IEEE出版】第三届信息技术与当代体育国际学术会议(TCS 2023) 2023 3rd International Conference on Information Technology and Contemporary Sports 2023年第三届信息技术与当代体育国际学术会议(TCS 2023)将于2023年12月2…

Dueling DQN 跑 Pendulum-v1

gym-0.26.1 Pendulum-v1 Dueling DQN 因为还是DQN,所以我们沿用double DQN,然后把 Qnet 换成 VAnet。 其他的不变,详情参考前一篇文章。 class VA(nn.Module):"""只有一层隐藏层的A网络和V网络"""def __init__(self, state_dim, hidd…

子目录文件夹图片汇总

import os import shutildef collect_images(source_folder, target_folder):# 遍历主文件夹及其所有子文件夹for root, dirs, files in

位1的个数

题目链接 位1的个数 题目描述 注意点 输入必须是长度为 32 的 二进制串 解答思路 位运算判断每一位是否为1 代码 public class Solution {// you need to treat n as an unsigned valuepublic int hammingWeight(int n) {int res 0;for (int i 0; i < 32; i) {res …

项目经理和产品经理该如何选择?

最近很多人咨询“项目经理跟产品经理该怎么选&#xff0c;我更适合哪个&#xff1f;”“项目经理跟产品经理哪个更有钱途 ”“项目经理转产品经理好转吗”等等&#xff0c;今天就一次性说清楚项目经理跟产品经理有什么区别&#xff0c;应该怎么选择。 不想看长篇大论的&#x…

Python+Pytest接口自动化之HTTP协议基础

HTTP协议简介 HTTP 即 HyperText Transfer Protocol&#xff08;超文本传输协议&#xff09;&#xff0c;是互联网上应用最为广泛的一种网络协议。所有的 WWW 文件都必须遵守这个标准。 设计 HTTP 最初的目的是为了提供一种发布和接收 HTML 页面的方法。HTTP 协议在 OSI 模型…

Kubernetes版本升级到v1.18.0方法

升级k8s版本才能使用kube-prometheus安装监控 1、查看集群状态 [rootk8s-master k8s-script]# kubectl get nodes NAME STATUS ROLES AGE VERSION k8s-master Ready master 5d22h v1.18.0 k8s-slave1 Ready <none> 4d10h v1.18.0 k…

ActiveMQ使用指南

介绍 ActiveMQ是Apache开源组织旗下的一个项目&#xff0c;是一个流行的开源消息中间件。它完全支持JMS1.1和J2EE1.4规范的JMS Provider实现&#xff0c;并且是纯Java开发的产品。ActiveMQ支持多种语言编写客户端&#xff0c;包括C,C,C#,Perl,PHP,Ruby,Ajax等&#xff0c;同时…

做数据分析为何要学统计学(6)——什么问题适合使用卡方检验?

卡方检验作为一种非常著名的非参数检验方法&#xff08;不受总体分布因素的限制&#xff09;&#xff0c;在工程试验、临床试验、社会调查等领域被广泛应用。但是也正是因为使用的便捷性&#xff0c;造成时常被误用。本文参阅相关的文献&#xff0c;对卡方检验的适用性进行粗浅…