【QT】系统-下
欢迎来到Cefler的博客😁
🕌博客主页:折纸花满衣
🏠个人专栏:QT
目录
- 👉🏻QThead
- run()
- 👉🏻QMutex
- 👉🏻QWaitCondition
- 👉🏻QUdpSocket
- 常用API
- 👉🏻QNetworkDatagram
- 👉🏻Udp实现客户端和服务端回显
👉🏻QThead
QThread是Qt框架中用于多线程编程的基类,它提供了创建和管理线程的能力。以下是QThread的一些常用API:
run()
根据官方文档,我们需要重写run函数
- 创建 QThread 的子类
通常,你会通过继承 QThread
并重写其 run()
方法来创建自定义的线程类。run()
方法包含了线程将要执行的代码。
#include <QThread>class WorkerThread : public QThread
{Q_OBJECTpublic:void run() override {// 在这里编写线程要执行的代码// 例如,一个耗时的循环for (int i = 0; i < 5; ++i) {QThread::sleep(1); // 模拟耗时操作qDebug() << "Working in thread" << QThread::currentThreadId();}}
};
- 创建并启动线程
然后,你可以创建 WorkerThread
的实例,并调用其 start()
方法来启动线程。start()
方法会自动调用 run()
方法。
#include <QCoreApplication>int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);WorkerThread *worker = new WorkerThread();worker->start(); // 启动线程// 主线程可以继续执行其他任务qDebug() << "Working in main thread" << QThread::currentThreadId();// 等待线程完成worker->wait();delete worker;return a.exec();
}
👉🏻QMutex
QMutex
是 Qt 框架中用于管理对共享资源的互斥访问的一个类。在多线程程序中,多个线程可能会尝试同时访问同一个资源(如内存中的变量、文件句柄等),这可能导致数据损坏或不一致。QMutex
提供了一种机制来确保在同一时间内只有一个线程可以访问特定的资源。
基本使用
- 包含头文件
在你的 Qt 项目中,使用 QMutex
之前需要包含相应的头文件:
#include <QMutex>
- 创建 QMutex 实例
你可以在你的类内部或外部创建一个 QMutex
的实例。通常,这个实例会被声明为类的成员变量,以便在整个类中都能访问它。
QMutex mutex;
或者作为类的成员:
class MyClass {
public:MyClass() {}void someFunction() {// 使用 mutex}private:QMutex mutex;
};
- 锁定和解锁
- 锁定(Lock):在访问共享资源之前,你需要锁定
QMutex
。这可以通过调用lock()
方法实现。如果QMutex
已经被另一个线程锁定,当前线程将被阻塞,直到QMutex
被解锁。
mutex.lock();
// 访问共享资源
// ...
- 解锁(Unlock):完成资源访问后,你需要通过调用
unlock()
方法来解锁QMutex
,以便其他线程可以访问该资源。
// 完成资源访问
mutex.unlock();
- 使用 QMutexLocker(推荐)
虽然直接调用 lock()
和 unlock()
方法可以工作,但 Qt 推荐使用 QMutexLocker
类来自动管理锁的获取和释放。QMutexLocker
是一个在构造时自动锁定 QMutex
,在析构时自动解锁 QMutex
的类。这有助于避免忘记解锁或在发生异常时未解锁的问题。
QMutex mutex;void someFunction() {QMutexLocker locker(&mutex);// 访问共享资源// ...// 当离开此作用域时,locker 对象被销毁,自动调用 unlock()
}
注意事项
- 避免死锁:确保在程序中不会以循环的方式锁定多个
QMutex
,这可能导致死锁。 - 锁粒度:尽量减小锁的范围,只在必要时锁定,以提高程序的并发性能。
- 锁的性能:虽然
QMutex
提供了必要的同步机制,但过多的锁操作可能会降低程序的性能。在设计多线程程序时,要仔细考虑锁的使用。
使用 QMutex
可以帮助你在 Qt 程序中安全地管理多线程对共享资源的访问。
👉🏻QWaitCondition
QWaitCondition是Qt框架中用于线程间同步的一个重要机制,它允许线程等待某个条件的发生,并在条件满足时被唤醒。以下是QWaitCondition的基本使用方法:
- 基本概念
- 条件变量:QWaitCondition本质上是一个条件变量,用于线程间的同步。
- 互斥锁:QWaitCondition通常与QMutex一起使用,以确保在访问共享资源或条件时的线程安全。
- 主要函数
- wait(QMutex* mutex, unsigned long time = ULONG_MAX):使当前线程在指定的互斥锁上等待条件的发生。如果条件未满足,线程将阻塞;如果指定了超时时间(time),则线程将在超时后继续执行。
- wakeOne():唤醒在QWaitCondition上等待的一个线程(如果有的话)。
- wakeAll():唤醒在QWaitCondition上等待的所有线程。
- 使用步骤
等待条件
- 锁定互斥锁:在调用wait()之前,必须首先锁定一个互斥锁,以保护共享资源和条件变量。
- 调用wait():调用QWaitCondition的wait()函数,传入之前锁定的互斥锁。wait()函数内部会释放互斥锁,使其他线程可以修改条件或共享资源。当条件满足时,或超时时间到达时,wait()函数将返回,并重新获取互斥锁。
- 检查条件:从wait()返回后,应检查条件是否真正满足,因为可能存在“虚假唤醒”(spurious wakeup)的情况。
- 解锁互斥锁:在继续执行之前,应解锁互斥锁。
唤醒线程
-
锁定互斥锁:在修改条件或共享资源之前,必须首先锁定互斥锁。
-
修改条件:根据需要进行操作,以改变条件的状态。
-
唤醒线程:使用wakeOne()或wakeAll()函数唤醒等待的线程。
-
解锁互斥锁:在完成操作后,应解锁互斥锁。
-
示例场景
场景一:单个线程等待条件
QWaitCondition condition;
QMutex mutex;
bool conditionMet = false;// 等待条件的线程
mutex.lock();
while (!conditionMet) {condition.wait(&mutex); // 等待条件发生// 处理条件满足后的操作
}
mutex.unlock();// 在其他线程中
mutex.lock();
conditionMet = true; // 设置条件满足
condition.wakeOne(); // 唤醒等待的线程
mutex.unlock();
场景二:多个线程等待同一个条件
QWaitCondition condition;
QMutex mutex;
int counter = 0;// 等待条件的线程1和线程2
mutex.lock();
while (counter < 10) { // 或其他条件condition.wait(&mutex); // 等待条件发生
}
mutex.unlock();// 在其他线程中
mutex.lock();
counter = 15; // 设置条件满足
condition.wakeAll(); // 唤醒所有等待的线程
mutex.unlock();
- 注意事项
- 虚假唤醒:由于系统调度等原因,线程可能会被虚假唤醒。因此,在wait()返回后,应检查条件是否真正满足。
- 超时等待:wait()函数支持超时等待,可以在条件长时间未满足时避免线程永久挂起。
- 线程安全:QWaitCondition和QMutex的所有操作都是线程安全的,可以在多线程环境中安全使用。
通过以上介绍,可以了解到QWaitCondition在Qt多线程编程中的基本使用方法和重要性。
👉🏻QUdpSocket
QUdpSocket是Qt框架中用于UDP网络通信的一个类,它继承自QAbstractSocket,提供了发送和接收UDP数据报的功能。UDP(User Datagram Protocol,用户数据报协议)是一种轻量级、不可靠、面向数据报的无连接协议,适用于对可靠性要求不高的场合。以下是对QUdpSocket的详细介绍:
一、基本概念
- UDP协议:UDP是一种无连接的协议,每个数据报都是独立传输的,不会建立持久的socket连接。因此,UDP通信具有较低的延迟和较高的灵活性,但数据可能会丢失、乱序或重复。
- QUdpSocket:QUdpSocket类用于在Qt应用程序中实现UDP网络通信。通过它,可以发送和接收UDP数据报,实现单播、广播和组播等多种通信模式。
二、主要功能
-
数据报发送
- QUdpSocket提供了多个重载的
writeDatagram
方法来发送数据报。这些方法允许你指定目标地址(IP地址)和端口号,并将数据(通常是QByteArray类型)发送到指定的UDP客户端。 - 数据报的长度一般建议不超过512字节,因为较大的数据报可能会被网络层分片,增加传输的复杂性和错误率。
- QUdpSocket提供了多个重载的
-
数据报接收
- 在接收数据报之前,通常需要使用
bind
方法将QUdpSocket绑定到一个端口上,以便监听传入的数据报。 - 当有数据报到达时,QUdpSocket会发出
readyRead
信号。你可以在相应的槽函数中使用readDatagram
或receiveDatagram
方法来读取数据报。 readDatagram
方法允许你指定一个缓冲区来接收数据报,并可选地获取发送方的地址和端口信息。
- 在接收数据报之前,通常需要使用
-
多播和广播
- QUdpSocket支持UDP多播和广播。通过调用
joinMulticastGroup
方法,可以将QUdpSocket加入到指定的多播组中,以便接收该组内的数据报。 - 要进行广播,只需将目标地址设置为特殊的广播地址(如255.255.255.255),然后发送数据报即可。在同一网络范围内的所有UDP客户端都可以接收到这个广播数据报。
- QUdpSocket支持UDP多播和广播。通过调用
三、使用注意事项
- 端口号选择:通常选择1024到65535之间的端口号进行UDP通信。1024以下的端口号通常保留给系统或特定应用程序使用。
- 数据报大小:如前所述,建议发送的数据报大小不超过512字节。如果需要发送更大的数据量,可以考虑将数据拆分成多个较小的数据报进行发送。
- 错误处理:在使用QUdpSocket进行UDP通信时,需要注意处理可能发生的错误情况,如网络不可达、端口已被占用等。可以通过检查QUdpSocket的状态或捕获相关的异常来进行错误处理。
四、示例代码
以下是一个简单的QUdpSocket使用示例,展示了如何发送和接收UDP数据报:
// 发送数据报
QUdpSocket udpSocket;
QByteArray datagram = "Hello, UDP!";
udpSocket.writeDatagram(datagram.data(), datagram.size(), QHostAddress::LocalHost, 12345);// 接收数据报
udpSocket.bind(QHostAddress::LocalHost, 54321);
connect(&udpSocket, &QUdpSocket::readyRead, this, &MyClass::readPendingDatagrams);void MyClass::readPendingDatagrams() {while (udpSocket.hasPendingDatagrams()) {QByteArray datagram;datagram.resize(udpSocket.pendingDatagramSize());QHostAddress sender;quint16 senderPort;udpSocket.readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);// 处理接收到的数据报...}
}
在这个示例中,首先创建了一个QUdpSocket对象,并使用writeDatagram
方法发送了一个UDP数据报。然后,使用bind
方法将QUdpSocket绑定到一个端口上,以便接收数据报。最后,通过连接readyRead
信号到槽函数readPendingDatagrams
来读取和处理接收到的数据报。
常用API
-
构造函数和析构函数
QUdpSocket(QObject *parent = nullptr)
: 构造函数,可以指定父对象。~QUdpSocket()
: 析构函数,清理资源。
-
绑定端口
bool bind(const QHostAddress &address = QHostAddress::Any, quint16 port = 0, BindMode mode = DefaultForPlatform)
: 将QUdpSocket绑定到指定的地址和端口上,以便接收数据报。
-
发送数据报
qint64 writeDatagram(const char *data, qint64 size, const QHostAddress &address, quint16 port)
: 发送一个数据报到指定的地址和端口。qint64 writeDatagram(const QByteArray &datagram, const QHostAddress &host, quint16 port)
: 发送一个QByteArray类型的数据报到指定的地址和端口。
-
接收数据报
qint64 pendingDatagramSize() const
: 返回下一个待接收的数据报的大小(字节)。qint64 readDatagram(char *data, qint64 maxSize, QHostAddress *address = nullptr, quint16 *port = nullptr)
: 读取一个数据报到指定的缓冲区,并可选地获取发送方的地址和端口。qint64 receiveDatagram(char *data, qint64 maxSize, QHostAddress *address = nullptr, quint16 *port = nullptr)
: 与readDatagram
类似,但在某些平台上可能更可靠。
-
连接和断开
- 注意:QUdpSocket是无连接的,因此没有像TCP那样的
connect
和disconnect
方法来建立或断开连接。但是,你可以通过bind
来监听端口,并通过writeDatagram
来发送数据报。
- 注意:QUdpSocket是无连接的,因此没有像TCP那样的
-
信号
void readyRead()
: 当有新的数据报到达时发出此信号。void errorOccurred(QAbstractSocket::SocketError socketError)
: 当发生错误时发出此信号,提供错误类型。
-
状态查询
QAbstractSocket::SocketState state() const
: 返回QUdpSocket的当前状态(如未连接、监听中等)。QAbstractSocket::SocketError error() const
: 返回最后发生的错误类型。
-
多播和广播
bool joinMulticastGroup(const QHostAddress &groupAddress)
: 将QUdpSocket加入到指定的多播组中。bool leaveMulticastGroup(const QHostAddress &groupAddress)
: 离开之前加入的多播组。
这些API提供了QUdpSocket进行UDP网络通信所需的基本功能。需要注意的是,由于UDP是无连接的协议,因此在使用QUdpSocket时,不需要像TCP那样建立连接,而是直接发送和接收数据报。
👉🏻QNetworkDatagram
QNetworkDatagram 简介
QNetworkDatagram
是 Qt 网络模块中的一个类,用于处理 UDP 数据报。它可以在无连接的情况下发送和接收数据,非常适合实时通信和小数据量的传输。
基本用法
-
创建 QNetworkDatagram:
你可以通过提供数据和目标地址来创建一个QNetworkDatagram
对象。QByteArray data = "Hello, UDP!"; QHostAddress address = QHostAddress::Broadcast; // 或者指定具体地址 quint16 port = 1234;QNetworkDatagram datagram(data, address, port);
-
发送数据报:
使用QUdpSocket
类来发送QNetworkDatagram
。QUdpSocket udpSocket; udpSocket.writeDatagram(datagram);
-
接收数据报:
设置一个QUdpSocket
来接收数据报,并在可读事件中处理它。QUdpSocket udpSocket; udpSocket.bind(QHostAddress::Any, port); // 绑定到指定端口connect(&udpSocket, &QUdpSocket::readyRead, [&]() {while (udpSocket.hasPendingDatagrams()) {QNetworkDatagram datagram = udpSocket.receiveDatagram();// 处理接收到的数据qDebug() << "Received:" << datagram.data() << "from" << datagram.senderAddress() << ":" << datagram.senderPort();} });
应用场景
- 实时通信:例如,在线游戏、视频会议等应用程序,使用 UDP 以减少延迟。
- 广播和多播:可以通过设置目标地址为广播地址或多播地址,向多个客户端发送数据。
- 传感器数据采集:在物联网应用中,常用于从传感器收集小量数据。
总结
QNetworkDatagram
是一个非常有用的类,可以简化 UDP 数据报的处理。结合 QUdpSocket
,你可以方便地实现高效的网络通信。需要注意的是,UDP 是无连接协议,因此不保证数据的可靠性或顺序。在设计应用时,应根据需求权衡使用。
👉🏻Udp实现客户端和服务端回显
代码链接:Udp实现客户端和服务端回显
实现效果:
如上便是本期的所有内容了,如果喜欢并觉得有帮助的话,希望可以博个点赞+收藏+关注🌹🌹🌹❤️ 🧡 💛,学海无涯苦作舟,愿与君一起共勉成长
相关文章:

【QT】系统-下
欢迎来到Cefler的博客😁 🕌博客主页:折纸花满衣 🏠个人专栏:QT 目录 👉🏻QTheadrun() 👉🏻QMutex👉🏻QWaitCondition👉🏻Q…...

java和kotlin 可以同时运行吗
Java 和 Kotlin 可以同时运行在同一个项目中,这主要得益于 Kotlin 对 Java 的互操作性。Kotlin 被设计为与 Java 100% 兼容,这意味着 Kotlin 代码可以很容易地调用 Java 代码,反之亦然。这种设计使得 Kotlin 能够无缝集成到现有的 Java 项目中…...

2024最新版 Tuxera NTFS for Mac 2023绿色版图文安装教程
在数字化时代,数据的存储和传输变得至关重要。Mac用户经常需要在Windows NTFS格式的移动硬盘上进行读写操作,然而,由于MacOS系统默认不支持NTFS的写操作,这就需要我们寻找一款高效的读写软件。Tuxera NTFS for Mac 2023便是其中…...

npm发布插件超级简单版
在开源的世界里,每个人都有机会成为贡献者,甚至是创新的引领者。您是否有过这样的想法:开发一个解决特定问题的小工具,让他成为其他开发者手中的利器?今天,我们就来一场实战训练,学习如何将你的…...

C# 访问Access存取图片
图片存入ole字段,看有的代码是获取图片的字节数组转换为base64字符串,存入数据库;显示图片是把base64字符串转换为字节数组再显示;直接存字节数组可能还好一点; 插入的时候用带参数的sql写法比较好;用拼接…...

正则表达式中常见字符的用法介绍
正则表达式(Regular Expression,简称Regex)是一种文本模式描述的方法,包括普通字符(如a到z之间的字母)和特殊字符(称为“元字符”)。正则表达式使用单个字符串来描述、匹配一系列符合…...

Vue3.0组合式API:依赖注入provide和inject实现跨层组件的通信
Vue3.0组合式API系列文章: 《Vue3.0组合式API:setup()函数》 《Vue3.0组合式API:使用reactive()、ref()创建响应式代理对象》 《Vue3.0组合式API:computed计算属性、watch监听器、watchEffect高级监听器》 《Vue3.0组合式API&…...

VSCode中配置C/C++环境
在Visual Studio Code(VSCode)中配置C/C环境是一个相对直接且功能强大的过程,它能让开发者利用VSCode的诸多便利功能来编写、编译和调试C/C代码。以下是一个详细的步骤指南,涵盖了从安装必要的软件到配置编译器、调试器以及VSCode…...

vue实现鼠标滚轮控制页面横向滑动
先看效果 20240919_095531 1.首先创建一个xScroll.vue组件 <template><div class"main" v-size-ob"mainSize"><div class"v-scroll"><div class"content"><slot></slot></div></div>…...

【Git使用】删除Github仓库中的指定文件/文件夹
前言: 上篇文章带大家上传了第一个项目至github,那要是想删除仓库中的指定文件夹怎么办?在Github中 仓库是无法通过鼠标操作直接删除文件和文件夹的,那只能通过 git 命令来执行删除操作。接下来就带大家进行操作。 详细步骤: 一…...

Iptables命令常用命令
前言:下是一些非常实用的 iptables 命令合集,涵盖网络攻击防护和日常网络安全防护 1. 查看当前规则 iptables -L -v -n查看现有的所有规则,-v 显示详细信息,-n 禁止解析IP地址和端口以加快显示速度。 2. 清空所有规则 iptables -F清除所有已…...

前端开发之原型模式
介绍 原型模式本质就是借用一个已有的实例做原型,在这原型基础上快速复制出一个和原型一样的一个对象。 class CloneDemo {name clone democlone(): CloneDemo {return new CloneDemo()} } 原型原型链 函数(class)都有显示原型 prototyp…...

分布式缓存服务Redis版解析与配置方式
一、Redis分布式缓存服务概述 Redis是一款高性能的键值对(Key-Value)存储系统,通常用作分布式缓存服务。它基于内存运行,支持丰富的数据类型,并具备高并发、低延迟的特点,非常适合用于缓存需要频繁访问的数…...

WordPress建站钩子函数及使用
目录 前言: 使用场景: 一、常用的wordpress钩子(动作钩子、过滤器钩子) 1、动作钩子(Action Hooks) 2、过滤器钩子(Filter Hooks) 二、常用钩子示例 1、添加自定义 CSS 和 JS…...

Qt 模型视图(二):模型类QAbstractItemModel
文章目录 Qt 模型视图(二):模型类QAbstractItemModel1.基本概念1.1.模型的基本结构1.2.模型索引1.3.行号和列号1.4.父项1.5.项的角色1.6.总结 Qt 模型视图(二):模型类QAbstractItemModel 模型/视图结构是一种将数据存储和界面展示分离的编程方法。模…...

算法打卡 Day41(动态规划)-理论基础 + 斐波那契数 + 爬楼梯 + 使用最小花费爬楼梯
文章目录 理论基础Leetcode 509-斐波那契数题目描述解题思路 Leetcode 70-爬楼梯题目描述解题思路 Leetcode 746-用最小花费爬楼梯题目描述解题思路 理论基础 动态规划,简称 DP,其中的每一个状态一定是由上一个状态推导出来的,而贪心算法没有…...

鸿蒙环境服务端签名直传文件到OSS
本文介绍如何在鸿蒙环境下将文件上传到OSS。 背景信息 鸿蒙环境是当下比较流行的操作环境,与服务端签名直传的原理类似,鸿蒙环境上传文件到OSS是利用OSS提供的PutObject接口来实现文件上传到OSS。关于PutObject的详细介绍,请参见PutObject。…...

计算机毕业设计Python+Flask微博情感分析 微博舆情预测 微博爬虫 微博大数据 舆情分析系统 大数据毕业设计 NLP文本分类 机器学习 深度学习 AI
首先安装需要的python库, 安装完之后利用navicat导入数据库文件bili100.sql到mysql中, 再在pycharm编译器中连接mysql数据库,并在设置文件中将密码修改成你的数据库密码。最后运行app.py,打开链接,即可运行。 B站爬虫数…...

solidwork剪裁实体
之前是这样: 效果如下:...

Junit与Spring Test简单使用
Junit与Spring Test简单使用 Junit5简介Junit5 注解Junit5与Spring结合 差异概览MockingMockBeanSpyBeanDemo 注意事项 又要写测试代码了,总结记录一下。 Junit5简介 与单一模块设计的Junit4不同,Junit5引入了模块化架构,由三个主要子项目组成: JUnit Pl…...

Vxe UI vue vxe-table 实现自适应列宽,根据内容自适应列的宽度
Vxe UI vue vxe-table 实现自适应列宽,根据内容自适应列的宽度 之前老版本是通过计算字符数量,然后给动态给每一列设置宽度,不仅麻烦,还不好复用。 看了 API 发现 v4.7 和 v3.9 版本已经直接就能支持了,只需加上 widt…...

document.visibilityState 监听浏览器最小化
1.document.hidden: 表示页面是否隐藏的布尔值。页面隐藏包括 页面在后台标签页中 或者 浏览器最小化 (注意,页面被其他软件遮盖并不算隐藏,比如打开的 sublime 遮住了浏览器)。 2.document.visibilityStateÿ…...

前端框架对比和选择
大家好,我是程序员小羊! 前言: 前端框架选择是前端开发中的关键决策,因为它影响项目的开发效率、维护成本和可扩展性。当前,最流行的前端框架主要包括 React、Vue 和 Angular。它们各有优劣,适用于不同…...

Linux 进程2
环境变量 再Linux操作系统中一切皆文件,这个环境变量自然也是一个文件,它的作用是辅助我们使用操作系统还可以辨识我们是什么用户(一般用户,root用户)。 env是读取完整环境变量的指令,里面记录了许多我登录操作系统所用的用户的信…...

WPF入门教学六 Grid布局进阶
在WPF(Windows Presentation Foundation)中,Grid布局是一种非常强大且灵活的布局控件,它允许你创建复杂的用户界面。以下是Grid布局的一些进阶技巧和教学: 一、基本概念回顾 Grid定义:Grid是一个用于布局…...

while循环及简单案例
//循环是流程控制中的一个重要分支 //流程控制 条件判断 循环 逻辑处理 //循环的目的和意义 //循环的目的是为了执行一块代码 //循环的意义是为了简化代码。增加代码的复用性 /* //例如输出0-100的数…...

电子看板实时监控数据可视化助力工厂精细化管理
在当今竞争激烈的制造业领域,工厂的精细化管理成为提高竞争力的关键。而电子看板实时监控数据可视化作为一种先进的管理工具,正为工厂的精细化管理带来巨大的助力。 一、工厂精细化管理的挑战 随着市场需求的不断变化和客户对产品质量要求的日益提高&am…...

邮储银行:面向金融行业的移动应用安全风险监测案例
本项目通过在移动应用中植入威胁情报探针并结合网络镜像流量方式,利用应用运行过程中设备、系统、应用、行为四个维度数据,将其与设备的关键因子关联生成唯一的移动设备指纹;对手机银行等应用资产进行资产台账梳理;结合服务端大数据分析平台的各种模型规则分析,实时监测移…...

ARMxy车辆数据采集Linux智能控制器
在当今科技日新月异的时代,高效智能的边缘计算设备在众多领域发挥着关键作用。我们的 ARM 边缘计算机,凭借其卓越的性能和广泛的适用性,成为车队管理智能化的核心力量。 一、强大硬件配置,完美适配车队管理需求 ARM 边缘计算机支…...

7.Java高级编程 多线程
Java高级编程 多线程 文章目录 Java高级编程 多线程一、进程与线程查看线程 二、线程创建方式三、线程状态四、线程常用方法五、线程安全 一、进程与线程 一个程序有一个进程 一个进程包含多个线程(必须有一个主线程) 并发: 在同一时刻&a…...