【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…...

7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...

Python实现prophet 理论及参数优化
文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候,写过一篇简单实现,后期随着对该模型的深入研究,本次记录涉及到prophet 的公式以及参数调优,从公式可以更直观…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...

C++使用 new 来创建动态数组
问题: 不能使用变量定义数组大小 原因: 这是因为数组在内存中是连续存储的,编译器需要在编译阶段就确定数组的大小,以便正确地分配内存空间。如果允许使用变量来定义数组的大小,那么编译器就无法在编译时确定数组的大…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek
文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama(有网络的电脑)2.2.3 安装Ollama(无网络的电脑)2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

招商蛇口 | 执笔CID,启幕低密生活新境
作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...
NPOI操作EXCEL文件 ——CAD C# 二次开发
缺点:dll.版本容易加载错误。CAD加载插件时,没有加载所有类库。插件运行过程中用到某个类库,会从CAD的安装目录找,找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库,就用插件程序加载进…...