【Java多线程(1)】创建线程的几种方式和Thread类及其常见方法

目录

一、Java创建线程的方式

1. 通过继承 Thread 类实现多线程

2. 通过实现 Runnable 接口实现多线程

3. 其他变形

二、Thread类及常见方法

1. Thread类的常见构造方法

2. Thread类的几个常见属性

2.1 getName()

2.2 setDaemon() & isDaemon()

2.3 isAlive() 

2.4 中断一个线程

示例一:使用自定义的变量来作为标志位

示例二:使用Thread对象的interrupted() 方法通知线程结束


一、Java创建线程的方式

在了解了进程和线程后,就可以开始学习Java多线程的内容了。

在 Java 中的线程(Thread)是由 Java 虚拟机(JVM)来管理和调度的,它们并不直接映射到操作系统的原生线程(OS Thread)。Java 线程是由 JVM 在后台使用一种称为“轻量级进程(Lightweight Process)”的概念来实现的。

1. 通过继承 Thread 类实现多线程

class MyThread extends Thread {@Overridepublic void run() {while (true) {System.out.println("Hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}public class Demo1 {public static void main(String[] args) {Thread t = new MyThread();t.start();while (true) {System.out.println("Hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
  • 这段代码定义了一个继承自 Thread 类的 MyThread 类(重写Thread类的run方法),该类会在运行时不断输出 "Hello thread",并且在每次输出后休眠1秒(Thread.sleep() 方法用于让当前线程暂停执行指定的时间(以毫秒为单位)。当调用 Thread.sleep() 方法时,当前线程会进入阻塞状态,并暂停执行指定的时间长度,然后重新进入就绪状态等待 CPU 时间片。)。同时,在 Demo1 类的 main 方法中创建了一个 MyThread 的实例 t,并启动了该线程(调用start()方法启动线程)。在主线程中也会不断输出 "Hello main",并且在每次输出后休眠1秒。
  • 当你运行这段代码时,将会看到两个线程(主线程和t的线程)同时执行,分别输出 "Hello thread" 和 "Hello main",并且它们会交替输出,因为两个线程是并发执行的。这样可以展示 Java 多线程编程中的基本概念和线程交替执行的特点。

2. 通过实现 Runnable 接口实现多线程

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

这段代码定义了一个实现了 Runnable 接口的 MyRun 类,其中重写了 run() 方法。其余内容与第一种一致。

上述两种方式的区别:

  1. 在创建实例时,第一种方式new的是子类MyThread的对象,第二种方式则是以MyRun的对象为参数创建Thread的对象。
  2. 第二种方式的好处是代码耦合度更低。run()方法中的代码表示多线程要做的事情,第二种方式相当于把自己要做的事情让别人帮你记录,第一种则是自己记录自己要做的事情。

3. 其他变形

注:这里的所有实现多线程的方式的代码效果都与前面一致:

  • 匿名内部类创建 Thread 子类对象
public class Demo3 {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();while (true) {System.out.println("Hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

对于只需要使用一次的类,我们就可以用匿名内部类的方式创建它的对象。

  •  匿名内部类创建 Runnable 实现类对象
public class Demo4 {public static void main(String[] args) {//匿名内部类创建线程Thread t = new Thread(new Runnable() {@Overridepublic void run() {while (true) {System.out.println("Hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}});t.start();while (true) {System.out.println("Hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
  •  Lambda表达式创建 Runnable 实现类对象
public class Demo5 {public static void main(String[] args) {//lambda表达式创建线程Thread t = new Thread(() -> {while (true) {System.out.println("Hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.start();while (true) {System.out.println("Hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

二、Thread类及常见方法

1. Thread类的常见构造方法

Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread("这是我的名字");
Thread t4 = new Thread(new MyRunnable(), "这是我的名字");

2. Thread类的几个常见属性

  • ID 是线程的唯⼀标识,不同线程不会重复
  • 名称是各种调试⼯具⽤到
  • 状态表⽰线程当前所处的⼀个情况(就绪、阻塞、运行态)
  • 优先级⾼的线程理论上来说更容易被调度到
  • 关于后台(守护)线程,需要记住⼀点:JVM会在⼀个进程的所有⾮后台线程结束后,才会结束运⾏。
  • 是否存活,即简单的理解,为 run ⽅法是否运⾏结束了
  • 线程的中断问题,下⾯我们进⼀步说明

下面是几个方法的示例:

2.1 getName()

public class Demo1 {public static void main(String[] args) {//名称 getName()Thread t = new Thread(() ->System.out.println(Thread.currentThread().getName()), "线程0");t.start();}
}

2.2 setDaemon() & isDaemon()

JVM会在⼀个进程的所有⾮后台线程结束后(即所有前台线程结束,后台线程也就结束),才会结束运⾏。每一个线程默认都是前台线程。

public class Demo2 {public static void main(String[] args) {//setDaemon() & isDaemon()Thread t = new Thread(() -> {for (int i = 0; i < 5; i++) {System.out.println("Hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});//设置为后台(守护)线程 当前台线程都执行结束,后台线程也要结束t.setDaemon(true);t.start();}
}

上述代码中,线程 t 是一个后台线程,它会输出 "Hello thread" 消息,然后休眠一秒钟,循环执行这个过程。但由于设置了守护线程,当主线程(前台线程)执行完毕时,即使后台线程 t 没有执行完毕,程序也会结束并退出。而这里前台线程很快就结束了,因此很大概率控制台是不会输入任何信息的。

2.3 isAlive() 

public class Demo3 {public static void main(String[] args) {//isAlive() 判断当前线程是否存活Thread t = new Thread(() -> {try {Thread.sleep(2000);} catch (InterruptedException e) {throw new RuntimeException(e);}});t.start();try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(t.isAlive());}
}

在这段代码中,通过 t.isAlive() 方法可以判断线程 t 是否处于存活状态。具体来说:

  1. 首先创建了一个线程 t,让它在启动后休眠 2000 毫秒(2秒)。
  2. 然后主线程休眠 3000 毫秒(3秒),等待线程 t 执行完成。
  3. 在主线程休眠结束后,通过 t.isAlive() 方法检查线程 t 是否仍然存活,即线程是否仍在执行中。

因为线程 t 在启动后休眠了 2 秒,而主线程休眠了 3 秒,所以在主线程判断线程 t 的存活状态时,线程 t 应该已经执行完毕,因此 t.isAlive() 返回 false。

2.4 中断一个线程

在多线程编程中,有时候需要取消一个正在执行的任务。通过中断线程可以有效地取消任务的执行,让线程尽快退出。有些场景下,需要控制线程的执行时间,避免线程长时间执行。通过设置超时时间并在超时后中断线程,可以有效控制线程的执行时间。
目前常见的中断线程有以下两种方式:
示例一:使用自定义的变量来作为标志位
public class Demo4 {//中断一个线程//方式一:使用自定义的变量来作为标志位(可能涉及到变量捕获)private static boolean isRunning = true;public static void main(String[] args) {Thread t = new Thread(() -> {while (isRunning) {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println(" t 进程结束了");});t.start();try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}//中断线程System.out.println("控制 t 进程结束");isRunning = false;}
}

这段代码展示了一种通过自定义变量来中断线程的方式。具体来说:

  1. 定义了一个静态的 boolean 类型变量 isRunning,初始值为 true。
  2. 在主线程中创建了一个新线程 t,该线程通过检查 isRunning 变量来确定是否继续执行。
  3. 线程 t 在循环中打印"hello thread",并每隔1秒执行一次。
  4. 主线程休眠3秒后,将 isRunning 设置为 false,以此来中断线程 t 的执行。

在这个例子中,通过设置 isRunning 变量为 false,可以使线程 t 在下一次循环时退出循环,从而结束线程的执行。这种方式是一种简单粗暴的中断线程的方法,但在实际应用中需要注意线程间状态同步和可见性等问题,避免出现并发安全性问题。

如果自定义变量不是成员变量,而是main当中的局部变量,这时候就可能涉及到变量捕获。

示例二:使用Thread对象的interrupted() 方法通知线程结束
public class Demo5 {//中断进程//方式二:使用Thread对象的Interrupted()方法通知线程结束public static void main(String[] args) {//isInterrupted() & Interrupted()Thread t = new Thread(() -> {while (!Thread.currentThread().isInterrupted()) {System.out.println("hello thread");try {Thread.sleep(10000);} catch (InterruptedException e) {//以 InterruptedException 异常的形式通知,清除中断标志//自行决定如何处理System.out.println("决定要执行哪些代码");break;}}});t.start();try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}//通知线程结束,将标志位设为truet.interrupt();}
}

这段代码展示了另一种中断线程的方式,通过使用Thread对象的interrupt()方法来通知线程结束。具体来说:

  1. 在主线程中创建了一个新线程 t,该线程在循环中通过检查Thread.currentThread().isInterrupted()方法的返回值来确定是否继续执行。
  2. 线程 t 在循环中打印"hello thread",并每隔10秒执行一次。
  3. 主线程休眠3秒后,调用t.interrupt()方法,向线程 t 发出中断通知。此时执行catch中的代码。

需要注意的是,当调用t.interrupt()方法后,并不会立即终止线程 t 的执行,而是设置了线程的中断标志位。即在调用Thread.sleep()方法的线程在睡眠期间被中断,就会导致InterruptedException异常的抛出。在catch块中捕获这个异常可以让程序员对线程中断进行相应的处理,比如释放资源、恢复状态等操作。

总结,thread 收到通知的方式有两种:

  1. 如果线程因为调⽤ wait/join/sleep 等⽅法⽽阻塞挂起,则以 InterruptedException 异常的形式通知,清除中断标志 当出现 InterruptedException 的时候, 要不要结束线程取决于 catch 中代码的写法. 可以选择忽 略这个异常, 也可以跳出循环结束线程.
  2. 否则,只是内部的⼀个中断标志被设置,thread 可以通过Thread.currentThread().isInterrupted() 判断指定线程的中断标志被设置,不清除中断标志。这种⽅式通知收到的更及时,即使线程正在 sleep 也可以⻢上收到。

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

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

相关文章

HTML5+CSS3+移动web——CSS进阶

系列文章目录 HTML5CSS3移动web——HTML 基础-CSDN博客https://blog.csdn.net/ymxk2876721452/article/details/136070953?spm1001.2014.3001.5502HTML5CSS3移动web——列表、表格、表单-CSDN博客https://blog.csdn.net/ymxk2876721452/article/details/136221443?spm1001.20…

复习Day3

1231. 航班时间 - AcWing题库 #include<bits/stdc.h> using namespace std; int getTime(){//得到时间 int h1,m1,s1,h2,m2,s2,d0;scanf("%d:%d:%d %d:%d:%d (%d)",&h1,&m1,&s1,&h2,&m2,&s2,&d);//补匹配直接跳过 int timed*24*3…

【Django开发】0到1美多商城项目md教程第3篇:用户注册业务实现,1. 用户注册页面绑定Vue数据【附代码文档】

美多商城完整教程&#xff08;附代码资料&#xff09;主要内容讲述&#xff1a;欢迎来到美多商城&#xff01;&#xff0c;项目准备。展示用户注册页面&#xff0c;创建用户模块子应用。用户注册业务实现&#xff0c;用户注册前端逻辑。图形验证码&#xff0c;图形验证码接口设…

《自动机理论、语言和计算导论》阅读笔记:p1-p4

《自动机理论、语言和计算导论》学习第1天&#xff0c;p1-p4&#xff0c;总计4页。这只是个人的学习记录&#xff0c;因为很多东西不懂&#xff0c;难免存在理解错误的地方。 一、技术总结 1.有限自动机(finite automata)示例 1.software for checking digital circuits。 …

IEEE TRANSACTIONS ON INTELLIGENT TRANSPORTATION SYSTEMS (T-ITS) 投稿记录,欢迎大家评论~

投稿整个流程时间点&#xff1a;Submitted: 17 October 2023 Awaiting Reviewer Assignment: From 18 October 2023 to 6 November 2023 Under review: From 6 November 2023 to 30 November 2023 Awaiting reviewer scores: From 1 December 2023 to 13 January 2024 Aw…

概率论与数理统计-条件概率题目2-3次事件依次发生问题

题目&#xff1a;设某光学仪器厂制造的透镜,第一次落下时打破的概率为1/2,若第一次落下未打破,第二次落下打破的概率为7/10,若前两次落下未打破,第三次落下打破的概率为9/10.试求透镜落下三次而未打破的概率&#xff1f; 解答&#xff1a; 设Ai表示透镜第i次下落打破&#xff…

【微服务】以模块化单体架构开发微服务应用

目录 推荐超级课程: Docker快速入门到精通Kubernetes入门到大师通关课AWS云服务快速入门实战我们知道,起初,单体应用有显著的优势:它们更容易开发和部署。从开发人员的角度来看,这种简单性是有益的。一切都是集中的,可以快速更新任何部分的业务逻辑并立即看到结果。这种开…

Redis八股

持久化 RDB AOF 数据过期策略 惰性删除 定期删除 数据淘汰策略 分布式锁 集群模型 主从同步原理 哨兵模式 分片集群 网络模型

哪本书最了解孩子?跟《米小圈上学记》一起做孩子的“引路人”!

孩子是发展中的人&#xff0c;需要家长的陪伴&#xff0c;孩子的身心发展是有规侓的&#xff0c;是处于发展过程中的人。我们要学会尊重孩子的发展&#xff0c;从兴趣出发&#xff0c;关注孩子的成长。但是&#xff0c;家长不可能无时无刻都能陪在孩子身边&#xff0c;他需要一…

day06vue2学习

day06 路由的封装抽离 问题&#xff1a;所有的路由配置都堆在main.js中不太合适么&#xff1f;不好&#xff0c;会加大代码的复杂度 目标&#xff1a;将路由模块抽离出来。好处&#xff1a;差分模块&#xff0c;利于维护。 大致的做法就是&#xff0c;将路由相关的东西都提…

使用docker-compose搭建wordpress博客

1、从远程仓库拉取worldpress镜像到本地 2、新建一个项目&#xff0c;然后在新建的项目目录里面新建一个docker-compose.yml模版文件。 3、编写docker-compose.yml文件 4、docker-compose up 运行项目。 5、在浏览器测试 使用docker-compose搭建wordpress博客实验成功。

03-SparkSQL入门

0 Shark Spark 的一个组件&#xff0c;用于大规模数据分析的 SQL 查询引擎。Shark 提供了一种基于 SQL 的交互式查询方式&#xff0c;可以让用户轻松地对大规模数据集进行查询和分析。Shark 基于 Hive 项目&#xff0c;使用 Hive 的元数据存储和查询语法&#xff0c;并基于Hiv…

基于Java中的SSM框架实现快餐店线上点餐系统项目【项目源码+论文说明】计算机毕业设计

基于Java中的SSM框架实现快餐店线上点餐系统演示 摘要 随着计算机互联网的高速发展。餐饮业的发展也加入了电子商务团队。各种网上点餐系统纷纷涌现&#xff0c;不仅增加了商户的销售量和营业额&#xff0c;而且为买家提供了极大的方便&#xff0c;足不出户&#xff0c;就能订…

arm 外部中断

main.c: #include"key_inc.h" //封装延时函数 void delay(int ms) {int i,j;for(i0;i<ms;i){for(j0;j<2000;j){}} } int main() {//按键中断的初始化key1_it_config();key2_it_config();key3_it_config();while(1){printf("in main pro\n");delay(1…

AugmentedReality之路-创建工程及主界面(1)

本文从零创建1个工程&#xff0c;并添加1个BasicMap和1个主界面&#xff0c;主界面包含Start AR和Stop AR两个按钮 1、创建移动端工程并打包运行 创建1个空工程&#xff0c;选择C&#xff0c;Mobile&#xff0c;Scalable&#xff0c;勾选StarterContent 通过Edit->Proje…

MySQL学习笔记(一)

1、什么是数据库&#xff1f;什么是数据库管理系统&#xff1f;什么是SQL&#xff1f;他们之间的关系是什么&#xff1f; 数据库&#xff1a;英文单词DataBase&#xff0c;简称DB。按照一定格式存储数据的一些文件的组合。顾名思义&#xff0c;存储数据的仓库&#xff0c;实际…

MySQL的基本操作与增删改查管理操作

一、MySQL数据库sql语句 1.1 sql 命令 database数据库table表row行column列user用户select从数据表中获取数据updata更新数据库中的数据delete从数据库中删除数据insert into 向数据表插入数据create database创建新数据库alter database修改数据库create table创建新表alter…

MySql实战--一条SQL查询语句是如何执行的?

平时我们使用数据库&#xff0c;看到的通常都是一个整体。比如&#xff0c;你有个最简单的表&#xff0c;表里只有一个ID字段&#xff0c;在执行下面这个查询语句时&#xff1a; select * from T where ID10&#xff1b; 我们看到的只是输入一条语句&#xff0c;返回一个结果…

WorkPlus一站式IM即时通讯解决方案,提升企业沟通效率与协作能力

在企业内部沟通与协作中&#xff0c;高效的即时通讯是实现团队协作与工作效率的重要保障。而WorkPlus以其稳定可靠的性能和全面的功能&#xff0c;为企业提供一站式的IM即时通讯解决方案&#xff0c;助力企业提升沟通效率与协作能力。IM即时通讯在企业中的重要性不言而喻。作为…

分享 | 使用Virtuoso VCPVSR工具基于标准单元的布局布线流程

​ 本节内容 导览 一、准备工作 二、运行VCP前的配置 三、VCP的布局规划 四、VCP的自动摆放 五、VSR的自动绕线 分享使用Virtuoso GXL Custom Digital Placer(VCP) & Space-based Router(VSR)工具进行基于纯数字Standard-Cell布局布线的操作流程。 VCP&VSR演…