【JavaEE】多线程(二)
多线程(二)
文章目录
- 多线程(二)
- 第一个多线程程序
- 观察线程
- sleep
- 创建线程
- 继承Thread类,重写run方法
- 实现Runnable, 重写run
- 继承Thread,重写run
- 实现Runnable,重写run
- 基于lambda表达式
- Thread的常见构造方法
- Thread几个常见属性
- setDaemon();
- isAlive();
续上文, 多线程(一),我们可以了解到,多线程和普通程序的区别:
- 每一个程序都是一个独立的执行流
- 多个线程之间都是“并发”执行的
第一个多线程程序
class MyTread extends Thread{@Overridepublic void run() {//这个方法就是线程的入口方法while (true) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("hello thread");}}
}//演示创建线程
public class Demo1 {public static void main(String[] args) throws InterruptedException {Thread t = new MyTread();//start 和 run 都是 Thread 的成员//run 只是描述了线程的入口(线程主要做什么任务)//start 则是真正调用了系统API,在系统中创建线程,让线程再调用run//t.start();//t.run();t.start();while (true){System.out.println("hello main");Thread.sleep(1000);}}
}
通过运行这个程序,我们可以发现两个while
循环在“同时执行”,可以看到打印结果是两边的日志在交替打印的:
这也说明了:
每个线程都是一个独立执行的逻辑,也就是独立的执行流~
我们也可以形象的看作是:兵分两路,并发执行~
而 并发 => 并行 + 并发
=> 并发编程的效果 => 充分使用多核cpu
资源
不过当我们将main
函数的t.start();改成t.run();
public static void main(String[] args) throws InterruptedException {Thread t = new MyTread();//start 和 run 都是 Thread 的成员//run 只是描述了线程的入口(线程主要做什么任务)//start 则是真正调用了系统API,在系统中创建线程,让线程再调用runt.run();while (true){System.out.println("hello main");Thread.sleep(1000);}}
此时,代码中不会创建新的线程,就只有一个主线程,这个主线程只能依次执行循环,执行完一个在执行另外一个~,不过因为代码中是while(true)
,是不会循环结束的,所以代码也就无法走到hello main
了。
还有一个需要我们了解的:main
这个线程是jvm
自动创建的,和其他线程比起来并没有什么特殊的,在一个java
进程中,至少都会有一个main
线程。
观察线程
当我们多线程程序运行的时候,我们可以使用IDEA
或者jconsole
来观察该进程内的多线程情况~
这里我们主要介绍jconsole
,jconsole
是jdk
自带的程序
而jconsole
我们可以在jdk
包里找到
-
首先我们要先找到
jdk
的安装地址 -
在地址处找到
jdk
然后打开bin
目录,然后再列表中找到jconsole.exe
这里我们要注意的是:
- 在启动
jconsole.exe
之前,我们得确保IDEA
的程序已经跑起来了
sleep
上述的线程,我觉得他在while
循环中转的太快了,想要他慢点~
那我们就可以使用Thread.sleep();
,sleep
是Thread
的静态方法
需要注意的是:我们使用sleep
的时候汇报这样的错误
MyRunnable
里的异常是受查异常,必须要显示处理,此处必须try catch
,不能用throws
,在这个代码中是重写父类的run
,父类的run
没有throws
,子类方法也就不能也有。
而这个程序每秒所打印出来的内容,顺序都是不确定的。
因为这两线程都是休眠1000ms
,当时间到后,谁先谁后是不一定的,这个过程可以视为“随机”
操作系统,对于多个线程的调度顺序,是不确定,随机的,而此处的随机不是数学上的概率均等的随机,是取决于 操作系统 对于线程调度的模块(调度器)来具体实现的~
创建线程
继承Thread类,重写run方法
package Thread.test_8_12;
class MyTread extends Thread{@Overridepublic void run() {//这个方法就是线程的入口方法while (true) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("hello thread");}}
}//演示创建线程
public class Demo1 {public static void main(String[] args) throws InterruptedException {Thread t = new MyTread();//start 和 run 都是 Thread 的成员//run 只是描述了线程的入口(线程主要做什么任务)//start 则是真正调用了系统API,在系统中创建线程,让线程再调用run//t.start();//t.run();t.start();while (true){System.out.println("hello main");Thread.sleep(1000);}}
}
实现Runnable, 重写run
package Thread.test_8_12;
class MyRunnable implements Runnable{@Overridepublic void run() {while (true) {System.out.println("hello world");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}public class Demo2 {public static void main(String[] args) throws InterruptedException {Runnable runnable = new MyRunnable();Thread t = new Thread(runnable);t.start();while (true){System.out.println("hello main");Thread.sleep(1000);}}
}
好处:
使用Runnable
的写法,和直接继承Thread
之间的区别就是:解耦合
使用Runnable
接口重写run
方法相对于直接继承Thread
类的方式更加灵活、可扩展,并且能够实现解耦合。我们可以将任务逻辑与线程的实现分离,通过实现Runnable
接口,我们能够更好地控制线程的行为并在需要时更好地管理和复用线程。
这里我们举个例子:
你老婆想要喝水,但是她又懒得去接水,于是她就会叫你或者你5岁孩子去接水。
而接水就是一个任务,你执行还是你孩子执行,这是没有本质区别的,那么此时我们就可以将接水这个任务单独提取为Runnable
,后续是谁都可以轻轻松松完成这个任务~
但是如果任务变了呢?接水 -> 泡茶
那么此时这个任务就只能你来完成了,你那5岁孩子完成不来任务
那么这个任务就是和你这个线程有一定耦合关系的~
而我们创建一个线程,需要进行两个关键操作:
-
明确线程要执行的任务
任务本身,不一定和线程 概念 强相关的
- 这个任务只是单纯的执行一段代码,这个任务是使用单个线程执行还是多个线程执行,亦或是别的方式(信号处理函数/协程/线程池)都没什么区别~
- 任务本身,就可以将任务本身提取出来,此时就可随时把代码改成使用其他方式来执行这个任务~
-
调用系统的
api
创建线程
继承Thread,重写run
package Thread.test_8_12;
public class Demo3 {public static void main(String[] args) {//匿名内部类Thread t = new Thread(){@Overridepublic void run() {while (true){System.out.println("hello world");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();}}}
}
**
**
实现Runnable,重写run
package Thread.test_8_12;
public class Demo4 {public static void main(String[] args) {Runnable runnable = new Runnable() {@Overridepublic void run() {while (true) {System.out.println("hello world");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}};Thread t = new Thread(runnable);t.start();while (true){System.out.println("hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}
基于lambda表达式
package Thread.test_8_12;
public class Demo6 {public static void main(String[] args) {Thread t = new Thread(()->{while (true){System.out.println("hello world");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
表达式不用重写run
方法?
实际上啊,lambda
自身就是run
方法
lambda
:本身就是用来表示逻辑的,使用lambda
就能描述出当前的线程要干嘛run
方法:线程的入口,通俗来说就是:告诉线程你要干啥子~
Thread的常见构造方法
方法 | 说明 |
---|---|
Thread() | 创建线程对象 |
Thread(Runnable target) | 使用Runnable对象创建线程对象 |
Thread(String name) | 创建线程对象,并命名 |
Thread(Runnable target, String name) | 使用Runnable对象创建线程对象,并命名 |
Thread(TreadGroup group, Runnable target) | 线程可以被用来分组管理,分好的几位线程组 |
public class Demo7 {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();}
Thread几个常见属性
属性 | 获取方法 |
---|---|
ID | getId() |
名称 | getName() |
状态 | getState() |
优先级 | getPriority() |
是否后台进程 | isDaemon() |
是否存活 | isAlive() |
是否被中断 | isInterrupted() |
setDaemon();
一般默认下,一个进程是前台线程~
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 为后台线程t.setDaemon(true);t.start();}
调用setDaemon(true)
方法,将t设置为后台线程。后台线程是一种特殊的线程,当所有前台线程(例如主线程)都结束时,后台线程会自动终止
改成后台线程之后,主线程飞快执行完了,此时就没有其他前台线程了,于是线程结束,t
线程来不及执行,就 over 了~~
isAlive();
Thread
对象的生命周期,要比系统内核中的线程更长一些~
Thread
对象还在,内核中的线程已经销毁了这样的情况(不求同年同月同日生,也不求i同年同月同日死)
所以我们可以使用isAlive();
来判定内核线程是不是已经没了,也就是回调函数执行完毕,线程就没了
package Thread;
public class Demo8 {public static void main(String[] args) {Thread t = new Thread(()->{System.out.println("线程开始");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程结束");});t.start();System.out.println(t.isAlive());try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(t.isAlive());}
}
下面用一张图概括一下本篇文章所讲的内容~
至此,多线程(二)先写到这,后续会持续更新,敬请期待~
相关文章:

【JavaEE】多线程(二)
多线程(二) 文章目录 多线程(二)第一个多线程程序观察线程sleep创建线程继承Thread类,重写run方法实现Runnable, 重写run继承Thread,重写run实现Runnable,重写run基于lambda表达式 T…...
OkHttp 根据服务器返回的的过期时间设置缓存
据返回的缓存时间来缓存响应,可以通过使用OkHttp的CacheControl和ResponseCacheInterceptor来实现。以下是一个示例代码: // 创建缓存目录和缓存对象 File cacheDirectory new File(context.getCacheDir(), "http-cache"); int cacheSize 1…...

智能远程监考方案助力企业考试化繁为简
在音视频数字化之旅中,轻装上阵。 近年来,在数字化浪潮之下,远程考试频繁成为各领域热词,各企业也纷纷改革求新,将原本的企业内部考试转移到线上,从而获取更低廉的组考成本,更高的管理效率&…...

基于matlab实现的额 BP神经网络电力系统短期负荷预测未来(对比+误差)完整程序分享
基于matlab实现的额 BP神经网络电力系统短期负荷预测 完整程序: clear; clc; %%输入矢量P(15*10) P[0.2452 0.1466 0.1314 0.2243 0.5523 0.6642 0.7105 0.6981 0.6821 0.6945 0.7549 0.8215 0.2415 0.3027 0; 0.2217 0.1581 0.1408 0.23…...
WPF的_Expander控件
WPF Expander 是 WPF(Windows Presentation Foundation)框架中的一个控件,用于实现可以展开和折叠内容的可折叠面板。 Expander 控件通常由一个展开/折叠的标题(Header)和一个显示/隐藏的内容部分(Content…...

【MT7628AN】IOT | MT7628AN OpenWRT开发与学习
IOT | MT7628AN OpenWRT开发与学习 时间:2023-06-21 文章目录 `IOT` | `MT7628AN` `OpenWRT`[开发与学习](https://blog.csdn.net/I_feige/article/details/132911634?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22132911634…...
基于Matlab实现自动泊车(垂直泊车)
自动泊车是一项非常有趣和实用的技术,它可以让车辆在没有人为干预的情况下自动停放在合适的位置上。在这篇文章中,我们将介绍如何使用Matlab实现自动泊车。 首先,我们需要了解自动泊车的基本原理。自动泊车系统通常包括车辆、传感器和控制算…...

笔试面试相关记录(4)
(1)实现防火墙的主流技术有哪些? 实施防火墙主要采用哪些技术 - 服务器 - 亿速云 (yisu.com) (2) char arr[][2] {a, b, c, d}; printf("%d", *(arr1)); 输出的是谁的地址?字符c 测试代码如下…...

unity UDP 通信
客户端 接收端 : using System; using System.IO; using System.Collections; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using UnityEngine; using UnityEngine.UI;public cla…...
一篇解决JavaScript
华子目录 JavaScript介绍JavaScript的组成JavaScript书写位置内部外部 js注释js输入(prompt)js输出js变量js基本数据类型number(数值类型)string(字符串)Boolean(布尔类型)undefined…...
Unity UGUI(一)基础组件
文章目录 1.Text:文本框2.Image:精灵图3.RawImage:生图4.Button:按钮5.InputField:输入框6.Tooggle:选择框7.Slider:滑动条8.Dropdown:下拉菜单9.Scrollbar:滚动条10.Scr…...

【微服务】六. Nacos配置管理
6.1 Nacos实现配置管理 配置更改热更新 在nacos左侧新建配置管理 Data ID:就是配置文件名称 一般命名规则:服务名称-环境名称.yaml 配置内容填写:需要热更新需求的配置 配置文件的id:[服务名称]-[profile].[后缀名] 分组&#…...

【华为云云耀云服务器L实例评测|云原生】自定制轻量化表单Docker快速部署云耀云服务器
🤵♂️ 个人主页: AI_magician 📡主页地址: 作者简介:CSDN内容合伙人,全栈领域优质创作者。 👨💻景愿:旨在于能和更多的热爱计算机的伙伴一起成长!!&…...

无涯教程-JavaScript - ACOTH函数
描述 ACOTH函数返回数字的反双曲余切。 语法 ACOTH (number)争论 Argument描述Required/OptionalNumberThe absolute value of Number must be greater than 1. i.e., Number must be must be less than -1 or greater than 1.Required Notes 用于计算双曲反余切的方程为-…...

Qt QTreeWidge解决setItemWidget后,导致复选框失效
一、问题: QTreeWidget某一项加上itemWidget后,导致复选框失效问题 二、解决方法 将要加上的widget控件加到该项的后续的列,即控件跟复选框不同一列 三、具体代码 QTreeWidget* treeW new QTreeWidget; treeW->setColumnCount(2); /…...

strncpy
strncpy: 函数介绍: 函数原型: char *strncpy(char *dest, const char *src, int n) 返回值:dest字符串起始地址 说明: 1、当src字符串长度小于n时,则拷贝完字符串后,剩余部分将用空字节填…...
c++学习【23】matlab实现FOC算法
% 创建Figure窗口和滑块 figure;Id_slider uicontrol(Style, slider, Position, [100 50 120 20], ...Min, -5, Max, 5, Value, 1.5, Callback, updateVoltage); Id_text uicontrol(Style, text, Position, [100 80 120 20], String, d轴电流: 1.5);Iq_slider uicontrol(Sty…...

2020-2023中国高等级自动驾驶产业发展趋势研究-概念界定
1.1 概念界定 自动驾驶发展过程中,中国出现了诸多专注于研发L3级以上自动驾驶的公司,其在业界地位也越来越重要。本报告围绕“高等级自动驾驶” 展开,并聚焦于该技术2020-2023年在中国市场的变化趋势进行研究。 1.1.1 什么是自动驾驶 自动驾驶…...
ICPC 2022 网络赛 h (模拟
#include<bits/stdc.h> using namespace std; using VI vector<int>; using ll long long; const int mod 20220911;//枚举数位,枚举这一位余数是几 //每一位的限制, //如果有repeat 就下一个 int change(string x){int res 0 ;for(int …...

如何保护您的工业网络?
工业网络通过连接机器、设备和系统,在实现工业流程的高效生产、监控和管理方面发挥着关键作用。保护工业网络,确保其关键资产和流程的完整性、可用性和机密性,是工业组织的首要任务。在本文中,我们将探讨保护工业网络安全面临的障…...
Cesium1.95中高性能加载1500个点
一、基本方式: 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...
JavaScript基础-API 和 Web API
在学习JavaScript的过程中,理解API(应用程序接口)和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能,使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...

使用LangGraph和LangSmith构建多智能体人工智能系统
现在,通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战,比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...
「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案
在移动互联网营销竞争白热化的当下,推客小程序系统凭借其裂变传播、精准营销等特性,成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径,助力开发者打造具有市场竞争力的营销工具。 一、系统核心功能架构&…...

【深度学习新浪潮】什么是credit assignment problem?
Credit Assignment Problem(信用分配问题) 是机器学习,尤其是强化学习(RL)中的核心挑战之一,指的是如何将最终的奖励或惩罚准确地分配给导致该结果的各个中间动作或决策。在序列决策任务中,智能体执行一系列动作后获得一个最终奖励,但每个动作对最终结果的贡献程度往往…...
游戏开发中常见的战斗数值英文缩写对照表
游戏开发中常见的战斗数值英文缩写对照表 基础属性(Basic Attributes) 缩写英文全称中文释义常见使用场景HPHit Points / Health Points生命值角色生存状态MPMana Points / Magic Points魔法值技能释放资源SPStamina Points体力值动作消耗资源APAction…...
Python第七周作业
Python第七周作业 文章目录 Python第七周作业 1.使用open以只读模式打开文件data.txt,并逐行打印内容 2.使用pathlib模块获取当前脚本的绝对路径,并创建logs目录(若不存在) 3.递归遍历目录data,输出所有.csv文件的路径…...
计算机系统结构复习-名词解释2
1.定向:在某条指令产生计算结果之前,其他指令并不真正立即需要该计算结果,如果能够将该计算结果从其产生的地方直接送到其他指令中需要它的地方,那么就可以避免停顿。 2.多级存储层次:由若干个采用不同实现技术的存储…...

欢乐熊大话蓝牙知识17:多连接 BLE 怎么设计服务不会乱?分层思维来救场!
多连接 BLE 怎么设计服务不会乱?分层思维来救场! 作者按: 你是不是也遇到过 BLE 多连接时,调试现场像网吧“掉线风暴”? 温度传感器连上了,心率带丢了;一边 OTA 更新,一边通知卡壳。…...