【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 …...
如何保护您的工业网络?
工业网络通过连接机器、设备和系统,在实现工业流程的高效生产、监控和管理方面发挥着关键作用。保护工业网络,确保其关键资产和流程的完整性、可用性和机密性,是工业组织的首要任务。在本文中,我们将探讨保护工业网络安全面临的障…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...
相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...
tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...
AGain DB和倍数增益的关系
我在设置一款索尼CMOS芯片时,Again增益0db变化为6DB,画面的变化只有2倍DN的增益,比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析: 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...
