【JavaEE】多线程 (1)

 

目录

1. 认识线程(Thread)

1) 线程是什么

2) 为啥要有线程 

3) 进程和线程的区别

2.第⼀个多线程程序

3.多线程的其他创建方式

方法二:实现 Runnable 接⼝

方法三:匿名内部类

 方法四:实现Runable, 重写run, 匿名内部类

方法五:使用lambda表达式 (常用到的写法)

2. Thread 类及常⻅⽅法

2.1 Thread 的常⻅构造⽅法

2.2 Thread 的⼏个常⻅属性

关于前台进程和后台进程:

使用 setDaemon(true) 可以将进程设为后台进程

isAlive()的作用

2.3 启动⼀个线程 - start()

面试题: start 和 run 的区别?

2.4 中断⼀个线程

2.5 等待⼀个线程 - join()

2.6 获取当前线程引⽤

2.7 休眠当前线程 

2.8 多线程的优势-增加运⾏速度

3. 线程的状态

3.1 观察线程的所有状态


1. 认识线程(Thread)

1) 线程是什么

⼀个线程就是⼀个 "执⾏流". 每个线程之间都可以按照顺序执⾏⾃⼰的代码. 多个线程之间 "同时" 执⾏ 着多份代码.

2) 为啥要有线程 

⾸先, "并发编程" 成为 "刚需".

• 单核 CPU 的发展遇到了瓶颈. 要想提⾼算⼒, 就需要多核 CPU. ⽽并发编程能更充分利⽤多核 CPU 资源.

• 有些任务场景需要 "等待 IO", 为了让等待 IO 的时间能够去做⼀些其他的⼯作, 也需要⽤到并发编程.

其次, 虽然多进程也能实现 并发编程, 但是线程⽐进程更轻量.

• 创建线程⽐创建进程更快.

• 销毁线程⽐销毁进程更快.

• 调度线程⽐调度进程更快.

3) 进程和线程的区别

• 进程是包含线程的. 每个进程⾄少有⼀个线程存在,即主线程。

• 进程和进程之间不共享内存空间. 同⼀个进程的线程之间共享同⼀个内存空间.

• 进程是系统分配资源的最⼩单位,线程是系统调度的最⼩单位。

• ⼀个进程挂了⼀般不会影响到其他进程. 但是⼀个线程挂了, 可能把同进程内的其他线程⼀起带⾛(整 个进程崩溃).

2.第⼀个多线程程序

感受多线程程序和普通程序的区别:

• 每个线程都是⼀个独⽴的执⾏流

• 多个线程之间是 "并发" 执⾏的.

package thread;
import static java.lang.Thread.sleep;
class MyThread extends Thread {@Overridepublic void run() {// run 方法就是该线程的入口方法while(true) {System.out.println("hello thread");try {sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
public class ThreadDemo1 {public static void main(String[] args) throws InterruptedException {//根据上面的类,创建出实例Thread t = new MyThread();// 调用 Thread 的start方法,才会真正调用系统 api , 在系统内核中创建出线程t.start();while(true) {System.out.println("hello main");sleep(1000);}}
}

在上面的代码中:

run 方法是线程的入口,每个线程跑起来,都会执行一些逻辑.

运行程序后,可以看出两个线程都在并发执行, t 线程打印 "hello tread" 语句, main 主线程打印 "hello main" 语句.

为什么"hello main" 会被先打印出来:

3.多线程的其他创建方式

方法二:实现 Runnable 接⼝

package thread;
class MyThread3 implements Runnable {@Overridepublic void run() {while(true) {System.out.println("hello runable");try {Thread.sleep(1000);}catch (InterruptedException e) {e.printStackTrace();}}}
}public class ThreadDemo3 {public static void main(String[] args) {Runnable runnable = new MyThread3();Thread t = new Thread(runnable);t.start();while(true) {System.out.println("hello main");try {Thread.sleep(1000);}catch (InterruptedException e) {e.printStackTrace();}}}
}

方法三:匿名内部类

package thread;
public class ThreadDemo4 {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);}}}
}

 方法四:实现Runable, 重写run, 匿名内部类

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

方法五:使用lambda表达式 (常用到的写法)

这中写法比较简洁:

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

2. Thread 类及常⻅⽅法

Thread 类是 JVM ⽤来管理线程的⼀个类,换句话说,每个线程都有⼀个唯⼀的 Thread 对象与之关 联。 ⽤我们上⾯的例⼦来看,每个执⾏流,也需要有⼀个对象来描述,类似下图所⽰,⽽ Thread 类的对象 就是⽤来描述⼀个线程执⾏流的,JVM 会将这些 Thread 对象组织起来,⽤于线程调度,线程管理。

2.1 Thread 的常⻅构造⽅法

 对于其中的两个方法:

实例:

package thread;
public class ThreadDemo7 {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) {e.printStackTrace();}}}},"这是我的线程");t.start();}
}

运行后, 使用jconsole来进行监视, 就可以更方便的查看我们运行的线程了:

2.2 Thread 的⼏个常⻅属性

• ID 是线程的唯⼀标识,不同线程不会重

 • 名称是各种调试⼯具⽤到

• 状态表⽰线程当前所处的⼀个情况,下⾯我们会进⼀步说明

• 优先级⾼的线程理论上来说更容易被调度到

• 关于后台线程,需要记住⼀点:JVM会在⼀个进程的所有⾮后台线程结束后,才会结束运⾏。

• 是否存活,即简单的理解,为 run ⽅法是否运⾏结束了

• 线程的中断问题,下⾯我们进⼀步说明

关于前台进程和后台进程:

我们代码创建的线程,默认就是前台线程,会阻止进程结束, 只要前台线程没有执行完, 进程就不会结束. 即使main已经执行完毕了.

package thread;
public class ThreadDemo7 {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) {e.printStackTrace();}}}},"这是我的线程");t.start();System.out.println("main 执行完毕");}
}

使用 setDaemon(true) 可以将进程设为后台进程

设为 true 是后台进程, 后台不会阻止进程结束

不设为 true 是前台 , 前台会阻止进程结束 

package thread;
public class ThreadDemo7 {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) {e.printStackTrace();}}}},"这是我的线程");//设置为后台进程t.setDaemon(true);t.start();System.out.println("main 执行完毕");}
}

isAlive()的作用

isAlive() 表示了内核中的线程 (pcb) 是否还存在, java代码中定义的线程对象 (Thread) 实例, 虽然表示一个线程, 这个对象本身的生命周期, 和内核中的pcb生命周期是不完全一样的.

package thread;
public class ThreadDemo8 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(()-> {// 这个线程的运行时间大概是1stry {Thread.sleep(1000);}catch (InterruptedException e) {e.printStackTrace();}});System.out.println("start 之前: " + t.isAlive());t.start();System.out.println("start 之后: " + t.isAlive());Thread.sleep(2000);// 2s 之后, 线程 t 已经结束了System.out.println("t 结束后: " + t.isAlive());}
}

2.3 启动⼀个线程 - start()

之前我们已经看到了如何通过覆写 run ⽅法创建⼀个线程对象,但线程对象被创建出来并不意味着线 程就开始运⾏了。

调⽤ start ⽅法, 才真的在操作系统的底层创建出⼀个线程.

调用start 创建出新的线程, 本质上是 start 会调用系统的 api , 来完成创建线程的操作.

面试题: start 和 run 的区别?

2.4 中断⼀个线程

如何中断一个线程呢?⽬前常⻅的有以下两种⽅式:

1. 通过共享的标记来进⾏沟通

2. 调⽤ interrupt() ⽅法来通知

 ⽰例-1: 使⽤⾃定义的变量来作为标志位.

public class ThreadDemo12 {private static boolean isQuit = false;public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {while(!isQuit) {System.out.println("我是一个线程,工作中!");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("线程工作完毕");});t.start();Thread.sleep(3000);System.out.println("让 t 线程结束");isQuit = true;}
}

注意: isQuit 不能是局部变量, 这里的变量如果是局部变量,必须是 final 或 "事实final"修饰, 由于此处的isQuit 要被修改, 不能写成 final 或 "事实final", 所以只能写成成员变量. 为啥写作成员变量就可以了呢? 因为lambda表达式本质是"函数式接口" ->匿名内部类, 内部类访问外部类的成员, 这是可以的.

⽰例-2: 使⽤ Thread.interrupted() 或者 Thread.currentThread().isInterrupted() 代替⾃定义标志位.

public class ThreadDemo13 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {while(!Thread.currentThread().isInterrupted()) {System.out.println("我是一个线程,工作中!");try {Thread.sleep(1000);}catch (InterruptedException e) {e.printStackTrace();// 加上 break ,此时抛出异常后,线程也会结束break;}}System.out.println("线程执行完毕");});t.start();Thread.sleep(3000);System.out.println("让t线程结束");//使用 interrupt 方法,来修改上面"Thread.currentThread().isInterrupted()"的值t.interrupt();}
}

2.5 等待⼀个线程 - join()

有时,我们需要等待⼀个线程完成它的⼯作后,才能进⾏⾃⼰的下⼀步⼯作。例如,张三只有等李四 转账成功,才决定是否存钱,这时我们需要⼀个⽅法明确等待线程的结束。

public class ThreadDemo14 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {for(int i = 0; i < 5; i++) {System.out.println("我是一个线程,正在工作中...");try {Thread.sleep(1000);}catch (InterruptedException e) {e.printStackTrace();}}System.out.println("线程执行结束");});t.start();t.join();System.out.println("这是主线程,期望这个日志在 t 结束后打印");}
}

 

在 main 线程中调用 t.join, 可以让 main 线程等待 t 线程结束.

执行 join 的时候, 就会看 t 线程是否在运行, 如果 t 运行中, main 线程就会阻塞(main 线程就暂时不去参与 cpu 执行了)

如果 t 运行结束, main 线程就会从阻塞中恢复过来, 并且继续往下执行.

任何一个线程都可以调用 join , 哪个线程调用 join , 那个线程就阻塞等待.

join的其他构造方法:

2.6 获取当前线程引⽤

如果是继承 Thread, 直接使用 this 拿到线程(Thread)的引用

package thread;
class MyThread extends Thread {@Overridepublic void run() {// 这个代码中,如果想要获取到线程的引用,直接使用 this 即可System.out.println(this.getId() + ", " + this.getName());}
}
public class ThreadDemo16 {public static void main(String[] args) {MyThread t1 = new MyThread();MyThread t2 = new MyThread();t1.start();t2.start();}
}

如果是 Runnable 或者 lambda 的方式, this 就无能为力了, 此时 this 已经不再指向 Thread 对象了. 

 就只能使用 Thread.currentThread() 了

package thread;
public class ThreadDemo17 {public static void main(String[] args) {Thread t1 = new Thread(() -> {Thread t = Thread.currentThread();System.out.println(t.getName());});Thread t2 = new Thread(() ->{Thread t = Thread.currentThread();System.out.println(t.getName());});t1.start();t2.start();}
}

2.7 休眠当前线程 

也是我们⽐较熟悉⼀组⽅法,有⼀点要记得,因为线程的调度是不可控的,所以,这个⽅法只能保证 实际休眠时间是⼤于等于参数设置的休眠时间的。

 

2.8 多线程的优势-增加运⾏速度

下面,我们将通过完成 1~100亿的相加运算, 来比较单个线程和多个线程之间共同执行的速度:

首先,单个线程来完成:

两个线程来共同完成:

注意, 此处的 result 已经溢出,不考虑 result 的准确性, 只关注运行的时间.

3. 线程的状态

3.1 观察线程的所有状态

package thread;
import static java.lang.Thread.sleep;
public class ThreadDemo18 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {for(int i = 0; i < 5; i++) {System.out.println("线程执行中...");try {sleep(1000);}catch (InterruptedException e) {e.printStackTrace();}}});//线程启动之前, 状态就是 NEWSystem.out.println(t.getState());t.start();System.out.println(t.getState());sleep(500);System.out.println(t.getState());t.join();//线程运行完毕, 状态就是 TERMINATEDSystem.out.println(t.getState());}
}

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

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

相关文章

带你用uniapp从零开发一个仿小米商场_2.创建空白项目及公共样式引入

创建空白项目 打开uniapp 点击新建->项目 如下, 是编辑你项目的名字的地方是你项目存放地址,可以点击浏览器去文件管理里面选地址是模板选择,这里选择默认模板就好是一些其他选择比如uvue能让你项目在编译成软件时运行更快,unicloud能让你用js写后端,且直接就是云开发,g…

Selenium+Pytest自动化测试框架实战

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

掌握3个Mock工具,轻松玩转单元测试

公司要求提升单元测试的质量&#xff0c;提高代码的分支覆盖率和行覆盖率&#xff0c;安排我研究单元测试&#xff0c;指定方案分享并在开发部普及开。 单元测试中的Mock的目的 Mock的主要目的是让单元测试Write Once, Run Everywhere. 即编写一次后&#xff0c;可以在任意时…

第十九章 解读利用pytorch可视化特征图以及卷积核参数(工具)

介绍一种可视化feaature maps以及kernel weights的方法 推荐可视化工具TensorBoard&#xff1a;可以查看整个计算图的数据流向&#xff0c;保存再训练过程中的损失信息&#xff0c;准确率信息等 学习视频&#xff1a; 使用pytorch查看中间层特征矩阵以及卷积核参数_哔哩哔哩…

【机器学习】算法性能评估常用指标总结

考虑一个二分问题&#xff0c;即将实例分成正类&#xff08;positive&#xff09;或负类&#xff08;negative&#xff09;。对一个二分问题来说&#xff0c;会出现四种情况。如果一个实例是正类并且也被 预测成正类&#xff0c;即为真正类&#xff08;True positive&#xff0…

文献速递:人工智能在新生儿重症监护室:现在是时候了

人工智能在新生儿重症监护室&#xff1a;现在是时候了 01 文献速递介绍 文章介绍了AI的多学科特性&#xff0c;包括计算机科学、数学、神经科学和哲学。AI的目标是通过各种计算和算法技术创建智能机器。尽管早期对人类水平AI的预测未能实现&#xff0c;但对AI的期待仍然强烈…

ubuntu20.04配置OpenCV的C++环境

ubuntu20.04配置OpenCV的C环境 这里以opencv-3.4.16为例 复现https://github.com/raulmur/ORB_SLAM2此项目&#xff0c;需安装opencv及其他依赖&#xff0c;可见README.md详情 1.下载opencv源代码 https://opencv.org/releases/ 2.下载OpenCV的扩展包opencv_contrib&#x…

python安装redis库

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

杂货铺 | Windows系统上解压缩tgz文件

文章目录 &#x1f4da;快速终端打开实现 & 解压缩实现步骤&#x1f4da;环境变量的一般配置步骤 & 问题解决思路 &#x1f4da;快速终端打开实现 & 解压缩实现步骤 将对应的tgz文件放入对应的文件夹。快速在指定文件夹下打开终端 打开对应的路径 双击地址栏 然后…

计算机视觉面试题-01

计算机视觉面试通常涉及广泛的主题&#xff0c;包括图像处理、深度学习、目标检测、特征提取、图像分类等。以下是一些可能在计算机视觉面试中遇到的常见问题&#xff1a; 图像处理和计算机视觉基础 图像是如何表示的&#xff1f; 图像在计算机中可以通过不同的表示方法&…

新手如何买卖可转债,可转债投资基础入门

一、教程描述 什么是可转债&#xff1f;可转债是可转换债券的二次简称&#xff0c;原始全称是可转换公司债券&#xff0c;这是一种可以在特定时间、按特定条件&#xff0c;转换为普通股票的特殊企业债券&#xff0c;可转换债券兼具债权和股权的特征&#xff0c;其英文为conver…

[网鼎杯 2020 朱雀组]Nmap

启动环境 结合题目首先就是要知道关于关于nmap命令 相关的命令-oN 标准保存 -oX XML保存 -oG Grep保存 -oA 保存到所有格式 -iL 读取文件内容&#xff0c;以文件内容作为搜索目标 -o 输出到文件 -sP Ping 扫描 还有许多 nmap命令https://blog.csdn.net/weixin_735627…

基于OGG实现Oracle实时同步MySQL

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&am…

学习Pandas 二(Pandas缺失值处理、数据离散化、合并、交叉表与透视表、分组与聚合)

文章目录 六、高级处理-缺失值处理6.1 检查是否有缺失值6.2 缺失值处理6.3 不是缺失值NaN&#xff0c;有默认标记的 七、高级处理-数据离散化7.1 什么是数据的离散化7.2 为什么要离散化7.3 如何实现数据的离散化 八、高级处理-合并8.1 pc.concat实现合并&#xff0c;按方向进行…

安卓用SQLite数据库存储数据

什么是SQLite&#xff1f; SQLite是安卓中的轻量级内置数据库&#xff0c;不需要设置用户名和密码就可以使用。资源占用较少&#xff0c;运算速度也比较快。 SQLite支持&#xff1a;null&#xff08;空&#xff09;、integer&#xff08;整形&#xff09;、real&#xff08;小…

Linux基本指令(前篇)

目录 1.ls指令 2.pwd指令 3.cd 指令 4.touch指令 5.mkdir指令&#xff08;重要&#xff09; 6.rmdir指令 && rm 指令&#xff08;重要&#xff09; 7.man指令&#xff08;重要&#xff09; 1.ls指令 ls 选项 目录或文件 对于目录&#xff0c;该命令列出该目录下的所…

【SpringCloud】微服务的扩展性及其与 SOA 的区别

一、微服务的扩展性 由上一篇文章&#xff08;没看过的可点击传送阅读&#xff09;可知&#xff0c; 微服务具有极强的可扩展性&#xff0c;这些扩展性包含以下几个方面&#xff1a; 性能可扩展&#xff1a;性能无法完全实现线性扩展&#xff0c;但要尽量使用具有并发性和异步…

4.28每日一题(二重积分比较大小:被积函数的大小、正负性、积分区间奇偶性)

一般比较大小的题目我们不需要把结果全部计算出来 &#xff0c;而是通过奇偶性或者被积函数的大小或大于0、等于0、小于0等方法判断比较

【Android Gradle】之Gradle入门及 wrapper 生成(一)

&#x1f604;作者简介&#xff1a; 小曾同学.com,一个致力于测试开发的博主⛽️&#xff0c;主要职责&#xff1a;测试开发、CI/CD 如果文章知识点有错误的地方&#xff0c;还请大家指正&#xff0c;让我们一起学习&#xff0c;一起进步。 &#x1f60a; 座右铭&#xff1a;不…

机器学习-激活函数的直观理解

机器学习-激活函数的直观理解 在机器学习中&#xff0c;激活函数&#xff08;Activation Function&#xff09;是用于引入非线性特性的一种函数&#xff0c;它在神经网络的每个神经元上被应用。 如果不使用任何的激活函数&#xff0c;那么神经元的响应就是wxb&#xff0c;相当…