Java多线程的创建与Thread类的方法及使用

news/2024/5/21 10:25:30/文章来源:https://blog.csdn.net/m0_59735420/article/details/127280879

Java多线程的创建与Thread类的方法及使用

  • 🍎一.Thread类的属性与方法
    • 🍇1.1什么是线程
    • 🍇1.2Thread类的基础常见的构造方法
    • 🍇1.3Thread的常见属性
    • 🍇1.4Thread类常用的基础方法
  • 🍎二.Java线程的创建
    • 🍇2.1继承Thread类来创建线程
    • 🍇2.2继承Runnable接口创建线程
    • 🍇2.3使用内部类创建线程
    • 🍇2.4使用Runnable作为对象来创建线程
    • 🍇2.5使用Lambda创建线程
  • 🍎三.Java多线程并发基础实现与Thread类常用方法
    • 🍇3.1多线程并发的简单实现
    • 🍇3.2多线程与单线程效率的区别
    • 🍇3.3查看Java线程内部基础属性
    • 🍇3.4线程的中断
    • 🍇3.5线程等待
    • 🍇3.6run方法与start方法的区别(面试问题)

🍎一.Thread类的属性与方法

🍇1.1什么是线程

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

举个例子如下场景:
一家公司要去银行办理业务,既要进行财务转账,又要进行福利发放,还得进行缴社保。如果只有张三一个会计就会忙不过来,耗费的时间特别长。为了让业务更快的办理好,张三又找来两位同事李四、王五一起来帮助他,三个人分别负责一个事情,分别申请一个号码进行排队,自此就有了三个执行流共同完成任务,但本质上他们都是为了办理一家公司的业务。此时,我们就把这种情况称为多线程,将一个大任务分解成不同小任务,交给不同执行流就分别排队执行。其中李四、王五都是张三叫来的,所以张三一般被称为主线程(Main Thread)。

🍇1.2Thread类的基础常见的构造方法

序号方法解释
1Thread()创建线程对象
2Thread(Runnable target)使用 Runnable 对象创建线程对象
3Thread(String name)创建线程对象,并命名
4Thread(Runnable target, String name)使用 Runnable 对象创建线程对象,并命名
5【了解】Thread(ThreadGroup group,Runnable target)线程可以被用来分组管理,分好的组即为线程组,这个目前我们了解即可
Thread t1 = new Thread();
//我们需要创建一个类来继承Runnable类,在实例化,下文在创建我会给读者朋友演示的
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread("这是我的名字");
Thread t4 = new Thread(new MyRunnable(), "这是我的名字");

🍇1.3Thread的常见属性

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

🍇1.4Thread类常用的基础方法

方法名解释
public void run()该方法用来封装线程运行时执行的内容
public synchronized void start()线程创建并执行run方法
public static native void sleep(long millis) throws InterruptedException使线程休眠millis毫秒(我们需要处理抛异常也可以用try catch)
public final void join() throws InterruptedException等待线程结束(在哪个线程中调用哪个对象的join方法,哪个线程就等待哪个对象)
public final synchronized void join(long millis) throws InterruptedException等待线程结束,()内可以添加你想等待的ms时间最多等待millis毫秒
public final synchronized void join(long millis, int nanos) throws InterruptedException指定最多等待时间等待线程,精确到纳秒
public void interrupt()中断线程对象所关联的对象,如果线程在休眠(阻塞状态)会抛出异常通知,否则设置中断标志位break
public static boolean interrupted()判断当前线程的中断标志位是否设置,调用后会清除线程的中断标志位
public boolean isInterrupted()判断当前线程的中断标志位是否设置,调用后不会影响线程的标志位
public final synchronized void setName(String name)修改线程对象名称
public static native Thread currentThread()获取当前线程对象

🍎二.Java线程的创建

🍇2.1继承Thread类来创建线程

//MyThread来继承Thread类并且重写了run方法
class MyThread extends Thread{@Overridepublic  void  run(){System.out.println("hello 1");}}
public class dome1 {public static void main(String[] args) {Thread thread = new MyThread();//记住只有执行start方法是线程才上真正的创建成功thread.start();}
}

在这里插入图片描述

🍇2.2继承Runnable接口创建线程

//MyRunnable来继承Runnable接口,并且在run方法中重写要执行的内容
class MyRunnable implements Runnable{@Overridepublic void run(){System.out.println("hellow 3");
}
public class dome3 {public static void main(String[] args) {Thread thread3 = new Thread(new MyRunnable());thread3.start();}
}

在这里插入图片描述

🍇2.3使用内部类创建线程

public class demo4 {public static void main(String[] args) {Thread thread4 = new Thread(){@Overridepublic void run(){System.out.println("hello 4");}};thread4.start();}
}

在这里插入图片描述

🍇2.4使用Runnable作为对象来创建线程

public class deom5 {public static void main(String[] args) {Thread thread5 = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("hello 5");}});thread5.start();}
}

在这里插入图片描述

🍇2.5使用Lambda创建线程

public class deom5 {public static void main(String[] args) {Thread thread5 = new Thread(()-> {System.out.println("hello 5");});thread5.start();}
}

在这里插入图片描述

🍎三.Java多线程并发基础实现与Thread类常用方法

🍇3.1多线程并发的简单实现

public class demo7 {public static void main(String[] args) {Thread thread7 = new Thread(new Runnable(){@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println("Thread线程在执行");try {Thread.sleep(1000);//休眠一秒,每过一秒打印一次} catch (InterruptedException e) {e.printStackTrace();}}}});thread7.start();for (int i = 0; i < 10; i++) {System.out.println("main线程在执行");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}

从上面的运行结果可以看出一个问题,因为thread线程与main线程都是每打印一句语句线程休眠1秒,两个线程唤醒的先后顺序是随机的,这也是java多线程中的一个“万恶之源”,这个问题给我们带来了很多麻烦,后续的博客我会细说
![在这里插入图片描述](https://img-blog.csdnimg.cn/d4f4a053100740e1b1433a220b0dcdb9.png

🍇3.2多线程与单线程效率的区别

public class demo8 {
//创建一个不可更改成员实数countpublic static final long count = 10_0000_0000;//多线程使用方法public static void thread() throws InterruptedException {//获取开始执行时间戳long start = System.currentTimeMillis();Thread thread1 = new Thread(new Runnable() {@Overridepublic void run() {int a = 0;for (int i = 0; i < count; i++) {a++;}}});thread1.start();Thread thread2 = new Thread(new Runnable() {@Overridepublic void run() {int b = 0;for (int i = 0; i < count; i++) {b++;}}});thread2.start();thread1.join();//需要抛异常thread2.join();//获取结束执行时间戳long end = System.currentTimeMillis();System.out.println("多线程执行时间:" + (end - start) + "ms");}//单线程方法public static void one(){//获取开始执行时间戳long start = System.currentTimeMillis();int a =0;for (int i = 0; i < count; i++) {a++;}int b =0;for (int j = 0; j < count; j++) {b++;}//获取结束执行时间戳long end = System.currentTimeMillis();System.out.println("单线程执行时间:" + (end - start) + "ms");}public static void main(String[] args) throws InterruptedException {//多线程thread();//因为thread方法执行了join方法所以要在main抛异常//单线程one();}
}

我们发现在执行大量计算执行结束后多线程的效率是比单线程执行的效率要快很多
在这里插入图片描述

🍇3.3查看Java线程内部基础属性

在我们下载好的jdk文件打开bin文件
在这里插入图片描述
找到这个文件双击
在这里插入图片描述
点击我们执行的文件
在这里插入图片描述
点击链接后,弹出这个页面,点击不安全链接
在这里插入图片描述
点击右上角线程这样我们就可以看到Java线程的一些属性
在这里插入图片描述

🍇3.4线程的中断

方法一:

public class deom9 {private static boolean quite = false;public static void main(String[] args) throws InterruptedException {Thread thread9 = new Thread(new Runnable() {@Overridepublic void run() {//我们让每次相隔1秒打印一次while (!quite) {System.out.println("一个不起眼的线程");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}});thread9.start();//main和线程一起执行,这样我们可以先限制5秒这样就可以先打印5次在终止了Thread.sleep(5000);quite = true;}
}

在这里插入图片描述
但是该方法是不够严谨的,有些场景可能达不到预期的效果,最优的做法就是调整线程对象或者线程类中的自带标志位
优化版本

public class demo10 {public static void main(String[] args) throws InterruptedException {Thread thread10 = new Thread(()->{while (!Thread.interrupted()) {System.out.println("一个不起眼的线程");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});thread10.start();//休眠5秒Thread.sleep(5000);//使用interrupt方法修改线程标志位,使其中断thread10.interrupt();}
}

在这里插入图片描述
我看到当我们进行5秒后的中断指令后发现线程还在继续执行,抛出一个InterruptedException异常后,线程没有中断,而是继续运行,原因是interrupt方法遇到因为调用 wait/join/sleep 等方法而阻塞的线程时会使sleep等方法抛出异常,并且中断标志位不会修改为true,这时我们的catch语句里面值输出了异常信息并没有去中断异常,所以我们需要在catch语句中加上线程结束的收尾工作代码和退出任务循环的break语句就可以了。
这样我们就可以看到我们已经成功中断线程执行了
在这里插入图片描述
方法二:
首先使用currentThread方法获取线程对象,然后再调用该对象中的isterrupted方法获取该对象的中断标志位代替我们自己所写的isQuit标志位,然后等该线程运行一段时间后使用interrupt方法改变标志位,中断线程,写出如下代码,看看能不能达到预期效果:
我们在实际使用中建议使用方法二

public class demo10 {public static void main(String[] args) throws InterruptedException {Thread thread10 = new Thread(()->{while (!Thread.currentThread().interrupted()) {System.out.println("一个不起眼的线程");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();System.out.println("线程终止");break;}}});thread10.start();//休眠5秒Thread.sleep(5000);//使用interrupt方法修改线程标志位,使其中断thread10.interrupt();}
}

在这里插入图片描述

🍇3.5线程等待

像上面的计算自增20亿次的例子就需要线程等待join方法,main线程需要等两个线程运行完毕后才能计算计算结束时的时间戳。
我们来假设几个线程,线程A表示调用join方法的线程,线程B表示join方法来自B线程对象,那么在A线程使用B.join方法,那就是A线程等待B线程结束

🍇3.6run方法与start方法的区别(面试问题)

当我们调用run方法就是单纯地调用了Thread对象中的一个重写普通方法而已,并没有创建一个新线程来执行run方法,而是通过main线程来执行的run方法,而使用start方法,会创建一个新线程并执行run方法。
注意:只有在执行线程的start方法时才是真正的完成创建线程

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

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

相关文章

6步搭建一个飞机大战游戏

摘要&#xff1a;本文以华为云软件开发平台DevCloud为例&#xff0c;展示飞机大战游戏开发的DevOps实践流程。DevOps实践 DevOps实践是一种开发、测试运维一体化的模式&#xff0c;其实践的外在表现一般包括了如代码仓库、构建、测试、发布、配置、监控等工具形成的一个完整的…

引擎之旅 Chapter.3 文件系统

文章目录引言在此之前...Unicode和ASCIIC风格字符串的操作函数集合字符串操作字符串类型转换Part1&#xff1a;操作文件名和文件路径Part2&#xff1a;单个文件的读写文件打开的模式TFile的定义Part3&#xff1a;异步文件I/O异步I/O线程文件类中的异步方法引言 为什么会将文件…

数据中台开源解决方案(一)

数据中台商业的解决方案有很多,开源框架种类繁多,每一个模块都有很多开源的套件。以查询引擎为例,可以使用的开源工具有MySQL、Redis、Impala、MongoDB、PgSQL等。可以根据实际业务需要,选择合适的开源套件。 可供选择的解决方案太多,重点推荐开源解决方案,框架图如下图所…

【RCJ-2 AC220V 0.015A静态冲击继电器】

系列型号 RCJ-2/48VDC冲击继电器 RCJ-2/110VDC冲击继电器 RCJ-2/220VDC冲击继电器 RCJ-2/100VAC冲击继电器 RCJ-2/127VAC冲击继电器 RCJ-2/220VAC冲击继电器 RCJ-3/220VAC冲击继电器 RCJ-3/127VAC冲击继电器 RCJ-3/100VAC冲击继电器 RCJ-3/220VDC冲击继电器 RCJ-3/110VDC冲击继…

【论文笔记】CIKM‘22 & Amazon | (Navip) 推荐系统中图神经网络的去偏邻域聚合

目录1. Introduction2. Related Work2.1 基于GNN的推荐2.2 逆倾向评分IPS3. 模型3.1 Navip4. 实验结果本文是亚马逊在CIKM 2022的一篇工作 论文地址&#xff1a;https://arxiv.org/pdf/2208.08847.pdf 问题 图形神经网络(GNN)对于曝光偏差的脆弱性导致模型偏差&#xff0c;产生…

09-Pytorch中的序列化容器

目录 1.梯度消失和梯度爆炸 1.1 梯度消失 1.2 梯度爆炸 1.3 解决梯度消失或梯度爆炸的经验 2.nn.Sequential 3.nn.BatchNorm1d 4.nn.Dropout 1.梯度消失和梯度爆炸 在使用pytorch中的序列化容器之前&#xff0c;我们先来了解一下常见的梯度消失和梯度爆炸的问题。 1.1 …

②、HTML 元素学习

HTML 元素 HTML 文档由 HTML 元素定义。 HTML 元素 *开始标签常被称为起始标签&#xff08;opening tag&#xff09;&#xff0c;结束标签常称为闭合标签&#xff08;closing tag&#xff09;。 HTML 元素语法 HTML 元素以开始标签起始HTML 元素以结束标签终止元素的内容是开始…

Class02

Class02 1.自动类型值转换 隐式类型转换 显式类型转换 隐式类型转换&#xff08;自动类型转换&#xff09; 隐式类型转换是小范围向大范围转换 实际上小范围包含在大范围内 整数类型的默认类型为int 浮点数类型的默认类型为double 显式类型转换&#xff08;强制类型转换&am…

linux 预读机制 (linux4.14)

一、基本概念 设计背景 文件一般是顺序访问的&#xff0c;访问[A, B]范围的数据后&#xff0c;接下来很可能访问[B1, BN]数据。由于访问磁盘、flash等存储器件比较耗时&#xff0c;在访问 [A, B]的时候&#xff0c;如果提前把[B1, BN]数据从存储器件读取到ram中&#xff0c;那…

TTN服务器LoRaWAN网关配置流程

在LoRaWAN物联网网络中&#xff0c;LoRaWAN网关起到了绝对核心的作用&#xff0c;它在整个网络中像是一座桥梁建立起网络服务器与终端节点的通信&#xff0c;下面我们将简单介绍如何使用E890-470LG11网关实现TTN服务器与终端节点的通信。 首先&#xff0c;在LoRaWAN通信中&…

亿佰特LoRaWAN入网TTN并订阅MQTT消息

一、LoRaWAN节点入网 1.注册并登录TTN账号。 2.添加网关。Gateway EUI可自定义。 3.切换到亿百特网关配置网页&#xff08;http://192.168.10.1/&#xff09;,配置网关。 4.切抱TTN网页&#xff0c;创建app&#xff0c;Application ID可任意取&#xff0c;只要不重复就行&…

MSRN(多尺度超分辨率重建)

目前的研究倾向于使用更深层次的卷积神经网络来提高性能。然而&#xff0c;盲目增加网络深度不能有效改善网络。更糟糕的是&#xff0c;随着网络深度的增加&#xff0c;训练过程中出现了更多的问题&#xff0c;需要更多的训练技巧。在本文中&#xff0c;我们提出了一种新颖的多…

TRC丨艾美捷TRC 1-Hexyl-2-phenyl-4说明书

1-Alkyl-2-aryl-4-(1-naphthoyl)pyrroles 作为大丨麻素 CB1 和 CB2 受体的高亲和力配体。 艾美捷TRC 1-Hexyl-2-phenyl-4化学性质&#xff1a; 目录号H297400 化学名称1-Hexyl-2-phenyl-4-(1-naphthoyl)pyrrole JWH-147 同义词JWH-147&#xff1b;(1-己基-5-苯基-1H-吡咯-3-基…

数据库06-Redis 强化

目录 Redis 强化 缓存使用原则 缓存淘汰策略 缓存穿透 缓存击穿 缓存雪崩 Redis持久化 RDB:(Redis Database Backup) AOF(Append Only File): Redis存储原理 Redis集群 Redis 强化 缓存使用原则 什么时候,什么样的数据能够保存在Redis中? 1.数据量不能太大 2.使…

计算机操作系统:输入输出管理知识点汇总(简化版)

说明 博客作为笔记备份&#xff0c;不定时更新参考内容为王道考研《计算机操作系统考研复习指导2023》简化版仅进行了知识点汇总&#xff0c;详细知识点没有记录&#xff0c;主要用来搭知识体系框架思维导图为文字版导出的&#xff0c;可将文字内容粘贴到思维导图软件自动生成…

DDL-操作表查询和创建

DDL-操作表查询以及创建 操作表 1.C(Create):创建语法:create table 表名(列名1 数据类型1,列名2 数据类型2,......列名n 数据类型n);注意:最后一列不需要加逗号 数据类型:int:整数类型age int,double:小数类型score double(5,2)date:日期类型,只包含年月日,yyyy…

二维数组、可变长数组、多维数组、函数调用

1.二维数组名称也可以代表数组里第一个存储区的地址。 例子&#xff1a; 输出结果&#xff1a;二维数组的名称二维数组第一个数的地址 二维数组的名称也不可以被赋值。 二维数组名称也可以进行sizeof计算&#xff0c;结果是二维数组里所有存储区的总大小。 例子&#xff1a;…

Spring源码深度解析:三、Spring之Bean的生命周期

Bean的生命周期 创建的生命周期 UserService---->推断构造---->普通对象----->依赖注入---->初始化前(PostConstruct)----->初始化(InitializingBean)---->初始化后(AOP)---->代理对象-----> 放入Map单例池中---->Bean对象 创建一个modle: spring…

Android 简单控件 - -4.按钮控件 button

系列文章目录 文章目录系列文章目录前言1.按钮控件Button1.1按钮控件的新增属性2. 点击事件和长按事件2.1 点击事件2.2 长按事件3. 禁用与恢复按钮总结前言 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 1.按钮控件Button 按钮控件Button由TextView派…

CPU有缓存一致性协议(MESI),为什么JMM还需要volatile关键字?

这是一个非常好的问题&#xff0c;相信本文能把这个问题讲得清清楚楚 上周我在查阅资料时无意中搜到一篇解释 volatile 用法的博文&#xff0c;这篇博文排得很靠前&#xff0c;不过很遗憾&#xff0c;虽然结论是对的&#xff0c;但分析过程完全错误&#xff0c;而且我发现网上…