【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());}
}
相关文章:

【JavaEE】多线程 (1)
目录 1. 认识线程(Thread) 1) 线程是什么 2) 为啥要有线程 3) 进程和线程的区别 2.第⼀个多线程程序 3.多线程的其他创建方式 方法二:实现 Runnable 接⼝ 方法三:匿名内部类 方法四:实现Runable, 重写run, 匿名内部类 方法五:使用lambda表达式…...
linux 应用层同步与互斥机制之条件变量
2、条件变量 互斥量防止多个线程同时访问同一共享变量。(我们称为互斥) 有一种情况,多个线程协同工作。一个线程的消费需要等待另一个线程的产出。必须线程B完成了应有的任务,满足了某一个条件,线程A才能继续执行。&…...

3.5毫米音频连接器接线方式
3.5毫米音频连接器接线方式 耳机插头麦克风插头 绘制电路图注意事项 3.5毫米音频连接器分为单声道开关型和无开关型如下图: sleeve(套筒) tip(尖端) ring(环) 耳机插头 麦克风插头 绘制电路图…...

智慧农田可视化大数据综合管理平台方案,EasyCVR助力农业高质量发展
一、背景需求 我国是农业大国,农业耕地面积达到20亿亩。随着物联网、大数据、人工智能等新一代信息技术与农业农村加速融合,以及国家对农业的重视,智慧农业对于我国农业现代化建设和实施乡村振兴战略具有重大引领与推动作用。在传统农田生产…...

python超详细基础文件操作【建议收藏】
文章目录 前言1 文件操作1.1 文件打开与关闭1.1.1 打开文件1.1.2 关闭文件 1.2 访问模式及说明 2 文件读写2.1 写数据(write)2.2 读数据(read)2.3 读数据(readlines)2.3 读数据(readline&#x…...

华为变革进展指数TPM的五个级别:试点级、推行级、功能级、集成级和世界级
华为变革进展指数TPM的五个级别:试点级、推行级、功能级、集成级和世界级 TPM(Transformation Progress Metrics,变革进展指标)用来衡量管理体系在华为的推行程度和推行效果,并找出推行方面的不足与问题,…...

vue el-select多选封装及使用
使用了Element UI库中的el-select和el-option组件来构建多选下拉框。同时,也包含了一个el-input组件用于过滤搜索选择项,以及el-checkbox-group和el-checkbox组件用于显示多选项。 创建组件index.vue (src/common-ui/selectMultiple/index.vue) <tem…...

大模型上下文学习(ICL)训练和推理两个阶段31篇论文
大模型都火了这么久了,想必大家对LLM的上下文学习(In-Context Learning)能力都不陌生吧? 以防有的同学不太了解,今天我就来简单讲讲。 上下文学习(ICL)是一种依赖于大型语言模型的学习任务方式…...
WordPress(安装比子主题文件)zibll-7.5.1
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、新建网站二、配置ssl三.配置伪静态四.上传文件五.添加本地访问前言 提示:这里可以添加本文要记录的大概内容: 首先,我们要先理解什么是授权原理。 原理就是我们大家运营网站,点击授权…...

蓝桥杯 动态规划
01 数字三角形 #include<bits/stdc.h> using namespace std; const int N105; using lllong long; ll a[N][N],dp[N][N]; int main(){int n;cin>>n;for(int i1;i<n;i){for(int j1;j<i;j){cin>>a[i][j];}}for(int i5;i>1;i--){for(int j1;j<i;j){…...

【图论】重庆大学图论与应用课程期末复习资料2-各章考点(计算部分)(私人复习资料)
图论各章考点 二、树1、避圈法(克鲁斯克尔算法)2、破圈法3、Prim算法 四、路径算法1、Dijkstra算法2、Floyd算法 五、匹配1、匈牙利算法(最大权理想匹配(最小权权值取反)) 六、行遍性问题1、Fleury算法&…...

整数和浮点数在内存中的存储(大小端详解)
目录 一、整数在内存中的存储 二、大小端字节序和字节序判断 2.1为什么有大小端? 2.2请简述大端字节序和小端字节序的概念,设计一个小程序来判断当前机器的字节序。(10分)-百度笔试题 方法一(char*强制类型转换)…...

SpringBoot 集成 ChatGPT,实战附源码
1 前言 在本文中,我们将探索在 Spring Boot 应用程序中调用 OpenAI ChatGPT API 的过程。我们的目标是开发一个 Spring Boot 应用程序,能够利用 OpenAI ChatGPT API 生成对给定提示的响应。 您可能熟悉 ChatGPT 中的术语“提示”。在 ChatGPT 或类似语…...

数据结构——希尔排序(详解)
呀哈喽,我是结衣 不知不觉,我们的数据结构之路已经来到了,排序这个新的领域,虽然你会说我们还学过冒泡排序。但是冒泡排序的性能不高,今天我们要学习的希尔排序可就比冒泡快的多了。 希尔排序 希尔排序的前身是插入排…...

C++ day53 最长公共子序列 不相交的线 最大子序和
题目1:1143 最长公共子序列 题目链接:最长公共子序列 对题目的理解 返回两个字符串的最长公共子序列的长度,如果不存在公共子序列,返回0,注意返回的是最长公共子序列,与前一天的最后一道题不同的是子序…...
ubuntu中删除镜像和容器、ubuntu20.04配置静态ip
1 删除镜像 # 短id sudo docker rmi 镜像id # 完整id sudo docker rmi 镜像id# 镜像名【REPOSITORY:TAG】 sudo docker rmi redis:latest2 删除容器 # 删除某个具体容器 sudo docker rm 容器id# 删除Exited状态/未运行的容器,三种命令均可 sudo docker rm docker …...

华为手环 8 五款免费表盘已上线,请注意查收
华为手环 8,作为一款集时尚与实用于一体的智能手环,不仅具备强大的功能,还经常更新的表盘样式,让用户掌控时间与健康的同时,也能展现自己的时尚品味。这不,12 月官方免费表盘又上新了,推出了五款…...

JOSEF约瑟 同步检查继电器DT-13/200 100V柜内安装,板前接线
系列型号 DT-13/200同步检查继电器; DT-13/160同步检查继电器; DT-13/130同步检查继电器; DT-13/120同步检查继电器; DT-13/90同步检查继电器; DT-13/254同步检查继电器; 同步检查继电器DT-13/200 100V柜内板前接线 一、用途 DT-13型同步检查继电器用于两端供电线路的…...

龙迅#LT8311X3 USB中继器应用描述!
1. 概述 LT8311X3是一款USB 2.0高速信号中继器,用于补偿ISI引起的高速信号衰减。通过外部下拉电阻器选择的编程补偿增益有助于提高 USB 2.0 高速信号质量并通过 CTS 测试。 2. 特点 • 兼容 USB 2.0、OTG 2.0 和 BC 1.2• 支持 HS、FS、LS 信令 • 自动检测和补偿 U…...
eclipse jee中 如何建立动态网页及服务的设置问题
第一次打开eclipse 时,设置工作区时,一定是空目录 进入后 File-----NEW------Dynamic Web Project 填 项目名,不要有大写 m1 next next Generate前面打对勾 finish 第一大步: window----Preferences type filter text 处填 :Serve…...

C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...
IGP(Interior Gateway Protocol,内部网关协议)
IGP(Interior Gateway Protocol,内部网关协议) 是一种用于在一个自治系统(AS)内部传递路由信息的路由协议,主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...
vue3 字体颜色设置的多种方式
在Vue 3中设置字体颜色可以通过多种方式实现,这取决于你是想在组件内部直接设置,还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法: 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...

视频字幕质量评估的大规模细粒度基准
大家读完觉得有帮助记得关注和点赞!!! 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用,因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型(VLMs)在字幕生成方面…...
相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...

使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
SQL慢可能是触发了ring buffer
简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...
Oracle11g安装包
Oracle 11g安装包 适用于windows系统,64位 下载路径 oracle 11g 安装包...