java并发编程 2:java线程基础知识

news2024/12/25 22:27:00

目录

  • 创建和运行线程
  • 查看进程线程
  • 线程运行原理
  • 常见方法了解
    • start与run
    • sleep与yield
    • 线程优先级
    • join
    • interrupt
    • 不推荐使用的方法
  • 主线程与守护线程
  • 线程状态
    • 操作系统中的线程状态
    • java中的线程状态

创建和运行线程

方法一: 直接使用 Thread

public class CreateThread01 {

    public static void main(String[] args) {
        Thread thread = new Thread() {
            @Override
            public void run() {
              	// run 方法内实现了要执行的任务
                System.out.println("线程执行啦");
            }
        };
        // 设置名称
        thread.setName("线程1");
        // 运行
        thread.start();

        System.out.println("主线程结束");
    }
}

方法二:使用 Runnable配合 Thread

public class CreateThread02 {

    public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("线程执行啦");
            }
        };

        // 创建线程
        Thread thread = new Thread(runnable, "线程2");
        // 执行
        thread.start();

        System.out.println("主线程结束");
    }
}

Java 8 以后可以使用 lambda 精简代码:

public class CreateThread02 {

    public static void main(String[] args) {
        // 创建线程
        Thread thread = new Thread(() -> System.out.println("线程执行啦"), "线程2");
        // 执行
        thread.start();

        System.out.println("主线程结束");
    }

这种方法把线程和任务分开了,用 Runnable 更容易与线程池等高级 API 配合。

方法三:FutureTask配合Thread

FutureTask 能够接收 Callable 类型的参数,主要用来处理有返回结果的情况,后面还会详细讲解。

示例:

public class CreateThread03 {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 创建FutureTask对象
        FutureTask<Integer> task = new FutureTask<>(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                System.out.println("线程开始啦");
                // 暂停1秒
                Thread.sleep(1000);
                return 100;
            }
        });


        // 创建线程
        Thread thread = new Thread(task, "线程1");
        // 执行
        thread.run();

        // 获取线程的结果,会等到线程结果返回才执行
        System.out.println(task.get());

        System.out.println("主线程结束");
    }
}

也可以使用lambda表达式简化:

public class CreateThread03 {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 创建FutureTask对象
        FutureTask<Integer> task = new FutureTask<>(() -> {
            System.out.println("线程开始啦");
            // 暂停1秒
            Thread.sleep(1000);
            return 100;
        });


        // 创建线程
        new Thread(task, "线程1").start();

        // 获取线程的结果,会等到线程结果返回才执行
        System.out.println(task.get());

        System.out.println("主线程结束");
    }
}

查看进程线程

linux

ps -fe 查看所有进程

ps -fT -p 查看某个进程(PID)的所有线程

kill 杀死进程

top 按大写 H 切换是否显示线程

top -H -p 查看某个进程(PID)的所有线程

Java

jps 命令查看所有 Java 进程

jstack 查看某个 Java 进程(PID)的所有线程状态

jconsole 来查看某个 Java 进程中线程的运行情况(图形界面)

线程运行原理

栈与栈帧

Java Virtual Machine Stacks (Java 虚拟机栈)

我们都知道 JVM 中由堆、栈、方法区所组成,其中栈内存是给谁用的呢?其实就是线程,每个线程启动后,虚拟机就会为其分配一块栈内存。

每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存

每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法

线程上下文切换(Thread Context Switch)

有时候线程并不是可以一直执行的,有一些原因会导致 cpu 不再执行当前的线程,转而执行另一个线程的代码:

  • 线程的 cpu 时间片用完
  • 垃圾回收
  • 有更高优先级的线程需要运行
  • 线程自己调用了 sleep、yield、wait、join、park、synchronized、lock 等方法

当发生线程切换时,并不是直接换另一个线程运行那么简单。为了下次再运行相同的线程,就需要上下文切换,也就是需要由操作系统保存当前线程的状态,并恢复另一个线程的状态。Java 中对应的概念就是程序计数器(Program Counter Register),它的作用是记住下一条 jvm 指令的执行地址,是线程私有的状态包括程序计数器、虚拟机栈中每个栈帧的信息,如局部变量、操作数栈、返回地址等。

需要知道的是线程上下文切换太频繁发生会影响程序性能。

常见方法了解

方法static功能说明注意
start()启动一个新线程,在新的线程运行 run 方法中的代码start 方法只是让线程进入就绪,里面代码不一定立刻运行(CPU 的时间片还没分给它)。每个线程对象的start方法只能调用一次,如果调用了多次会出现IllegalThreadStateException
run()新线程启动后会调用的方法如果在构造 Thread 对象时传递了 Runnable 参数,则线程启动后会调用 Runnable 中的 run 方法,否则默认不执行任何操作。但可以创建 Thread 的子类对象,来覆盖默认行为。
join()等待线程运行结束
join(long n)等待线程运行结束,最多等待 n 毫秒
getId()获取线程长整型的 idid 唯一
getName()获取线程名
setName(String)设置修改线程名
getPriority()获取线程优先级
setPriority(int)设置线程优先级java中规定线程优先级是1~10 的整数,较大的优先级能提高该线程被 CPU 调度的机率
getState()获取线程状态Java 中线程状态是用 6 个 enum 表示,分别为:NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED
isInterrupted()判断是否被打断不会清除 打断标记
isAlive()线程是否存活(还没有运行完毕)
interrupt()打断线程如果被打断线程正在 sleep,wait,join 会导致被打断的线程抛出 InterruptedException,并清除 打断标记 ;如果打断的正在运行的线程,则会设置 打断标记 ;park 的线程被打断,也会设置 打断标记
interrupted()static
static判断当前线程是否被打断会清除 打断标记
currentThread()static获取当前正在执行的线程
sleep(long n)static让当前执行的线程休眠n毫秒,休眠时让出 cpu 的时间片给其它线程
yield()static提示线程调度器让出当前线程对CPU的使用主要是为了测试和调试

start与run

调用run

示例:

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class CreateThread01 {

    public static void main(String[] args) {
        Thread thread = new Thread() {
            @Override
            public void run() {
                log.info("子线程执行啦");
            }
        };
        thread.setName("线程1");
        thread.run();

        log.info("主线程结束");
    }
}

结果如下:

2023-04-16 15:59:16,712 - 0    INFO  [main] up.cys.chapter01.CreateThread01$1:18  - 子线程执行啦
2023-04-16 15:59:16,719 - 7    INFO  [main] up.cys.chapter01.CreateThread01:27  - 主线程结束

根据打印的日志看到"子线程执行啦"虽然打印出来了,但是其执行是在[main]线程中。

调用start

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class CreateThread01 {

    public static void main(String[] args) {
        Thread thread = new Thread() {
            @Override
            public void run() {
                log.info("子线程执行啦");
            }
        };
        thread.setName("线程1");
        thread.start();

        log.info("主线程结束");
    }
}

打印如下:

2023-04-16 16:01:53,905 - 0    INFO  [main] up.cys.chapter01.CreateThread01:27  - 主线程结束
2023-04-16 16:01:53,905 - 0    INFO  [线程1] up.cys.chapter01.CreateThread01$1:18  - 子线程执行啦

根据打印的日志看到"子线程执行啦"是在[线程1]中执行的,这才是真的开启开启一个线程了。

结论

  • 直接调用 run 是在主线程中直接执行了 run方法,会把run 方法当成一个main 线程下的普通方法去执行,没有启动新的线程
  • 使用 start 会启动一个线程并使线程进入了就绪状态,当分配到 时间片 后就可以开始运行了

一般开发人员使用start来启动线程。

sleep与yield

sleep

  1. 调用 sleep 会让当前线程从 Running 进入 Timed Waiting 状态(阻塞)
  2. 其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出 InterruptedException
  3. 睡眠结束后的线程未必会立刻得到执行
  4. 建议用 TimeUnit 的 sleep 代替 Thread 的 sleep 来获得更好的可读性

yield

  1. 调用 yield 会让当前线程从 Running 进入 Runnable 就绪状态,然后调度执行其它线程
  2. 具体的实现依赖于操作系统的任务调度器

线程优先级

  1. 线程优先级会提示(hint)调度器优先调度该线程,但它仅仅是一个提示,调度器可以忽略它
  2. 如果 cpu 比较忙,那么优先级高的线程会获得更多的时间片,但 cpu 闲时,优先级几乎没作用

join

join会使当前线程等待指定线程运行结束才继续向下运行。

前面我们使用过sleep来让主线程等待一定时间,如下:

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;

@Slf4j
public class JoinTest01 {
    static int r = 0;

    public static void main(String[] args) throws InterruptedException {
        test1();
    }

    private static void test1() throws InterruptedException {
        log.info("开始");
        Thread t1 = new Thread(() -> {
            log.info("开始");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("结束");
            r = 10;
        });
        t1.start();

        // 这里的等待2秒时为了等子线运行完程,这样拿到的r值才是被修改后的
        TimeUnit.SECONDS.sleep(2);
        log.info("结果为:{}", r);
        log.info("结束");
    }
}

但是这样有个坏处就是,我们不知道线程需要等多久。这时候就需要使用join:

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;

@Slf4j
public class JoinTest01 {
    static int r = 0;

    public static void main(String[] args) throws InterruptedException {
        test1();
    }

    private static void test1() throws InterruptedException {
        log.info("开始");
        Thread t1 = new Thread(() -> {
            log.info("开始");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("结束");
            r = 10;
        });
        t1.start();
        // 使用join让主线程等待子线程结束
        t1.join();

        log.info("结果为:{}", r);
        log.info("结束");
    }
}

注意:用 join,需加在 t1.start() 之后。

join还可以带一个参数,表示只等待多久,这里就不在演示。

interrupt

此方法用来打断线程。但是打断不同状态的线程效果是不一样的。

1.打断sleep,wait,join的线程

这几个方法都会让线程进入阻塞状态。打断 sleep 的线程, 会清空打断状态,以 sleep 为例。

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;

@Slf4j
public class InterruptTest {
    public static void main(String[] args) throws InterruptedException {
        test1();
    }

    private static void test1() throws InterruptedException {
        Thread t1 = new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t1");
        t1.start();
        TimeUnit.SECONDS.sleep(1);
        t1.interrupt();
        log.info(" 打断状态: {}", t1.isInterrupted());
    }
}

输出:

java.lang.InterruptedException: sleep interrupted
	at java.base/java.lang.Thread.sleep(Native Method)
	at java.base/java.lang.Thread.sleep(Thread.java:339)
	at java.base/java.util.concurrent.TimeUnit.sleep(TimeUnit.java:446)
	at up.cys.chapter01.InterruptTest.lambda$test1$0(InterruptTest.java:21)
	at java.base/java.lang.Thread.run(Thread.java:834)
2023-04-16 17:06:30,827 - 0    INFO  [main] up.cys.chapter01.InterruptTest:29  -  打断状态: false

2.打断正常运行的线程

打断正常运行的线程, 不会清空打断状态:

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;

@Slf4j
public class InterruptTest02 {
    public static void main(String[] args) throws InterruptedException {
        test1();
    }

    private static void test1() throws InterruptedException {
        Thread t2 = new Thread(()->{
            while(true) {
                // 获取打断标记
                Thread current = Thread.currentThread();
                boolean interrupted = current.isInterrupted();
                // 如果标记为true,停止
                if(interrupted) {
                    log.info(" 打断状态: {}", interrupted);
                    break;
                }
            }
        }, "t2");
        t2.start();
        Thread.sleep(500);
        // 主线程打断子线程:注意并不会真正的打断,自线程还是会正常运行,需要自线程获取标记自己决定是否中断
        log.info("打断");
        t2.interrupt();
    }
}

输出如下:

2023-04-16 17:20:48,575 - 0    INFO  [main] up.cys.chapter01.InterruptTest02:34  - 打断
2023-04-16 17:20:48,588 - 13   INFO  [t2] up.cys.chapter01.InterruptTest02:26  -  打断状态: true

不推荐使用的方法

还有一些不推荐使用的方法,这些方法已过时,容易破坏同步代码块,造成线程死锁:

stop() 、suspend()、resume()

主线程与守护线程

默认情况下,Java 进程需要等待所有线程都运行结束,才会结束。有一种特殊的线程叫做守护线程,只要其它非守

护线程运行结束了,即使守护线程的代码没有执行完,也会强制结束。

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class DaemonTest {
    public static void main(String[] args) throws InterruptedException {
        test1();
    }

    private static void test1() throws InterruptedException {
        log.debug("开始运行...");
        Thread t1 = new Thread(() -> {
            log.info("开始运行...");
            try {
                Thread.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("运行结束...");
        }, "daemon");
        // 设置该线程为守护线程
        t1.setDaemon(true);
        t1.start();
        Thread.sleep(1);
        log.info("运行结束...");
    }
}

输出如下:

2023-04-17 00:09:48,359 - 0    INFO  [daemon] up.cys.chapter01.DaemonTest:19  - 开始运行...
2023-04-17 00:09:48,359 - 0    INFO  [main] up.cys.chapter01.DaemonTest:31  - 运行结束...

垃圾回收器线程就是一种守护线程;

Tomcat 中的 Acceptor 和 Poller 线程都是守护线程,所以 Tomcat 接收到 shutdown 命令后,不会等待它们处理完当前请求

线程状态

操作系统中的线程状态

这是从 操作系统 层面来描述的。

在这里插入图片描述

  • 【初始状态】仅是在语言层面创建了线程对象,还未与操作系统线程关联
  • 【可运行状态】(就绪状态)指该线程已经被创建(与操作系统线程关联),可以由 CPU 调度执行
  • 【运行状态】指获取了 CPU 时间片运行中的状态,当 CPU 时间片用完,会从【运行状态】转换至【可运行状态】,会导致线程的上下文切换
  • 【阻塞状态】如果调用了阻塞 API,如 BIO 读写文件,这时该线程实际不会用到 CPU,会导致线程上下文切换,进入阻塞状态;等 BIO 操作完毕,会由操作系统唤醒阻塞的线程,转换至【可运行状态】;与【可运行状态】的区别是,对【阻塞状态】的线程来说只要它们一直不唤醒,调度器就一直不会考虑调度它们
  • 【终止状态】表示线程已经执行完毕,生命周期已经结束,不会再转换为其它状态

java中的线程状态

这是从 Java API 层面来描述的,根据 Thread.State 枚举,分为六种状态

在这里插入图片描述

  • NEW :线程刚被创建,但是还没有调用 start() 方法
  • RUNNABLE :当调用了 start() 方法之后,注意,Java API 层面的 RUNNABLE 状态涵盖了 操作系统 层面的可运行状态】、【运行状态】和【阻塞状态】(由于 BIO 导致的线程阻塞,在 Java 里无法区分,仍然认为是可运行)
  • BLOCKED , WAITING , TIMED_WAITING :都是 Java API 层面对【阻塞状态】的细分
  • TERMINATED :当线程代码运行结束

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/688810.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用U盘安装Centos7全流程分享

文章目录 1、下载 centos7 的镜像2、下载老白菜3、插入U盘4、将U盘插入要刷机的电脑中5、获取U盘的启动地址6、正式配置重启7、进入安装界面了&#xff0c;现在就容易7.1 选择中文7.2 点击安装位置&#xff0c;分配磁盘7.3 配置网络和主机7.4 选择开始安装&#xff0c;并配置账…

【第1集】odoo16开发环境搭建

因为博主使用Mac作为开发电脑&#xff0c;因此都以Mac为主。同时本文odoo使用的是16版本&#xff0c;采用python源码进行安装&#xff0c;如需要二进制安装同学&#xff0c;后续有条件可能会出这方面的搭建指导。本文包含四个部分&#xff0c;分别为数据库安装&#xff0c;系统…

C++ bool 类型

文章目录 一. bool 类型二. 三目运算符 一. bool 类型 在 C 中&#xff0c;bool 类型用于表示逻辑值&#xff0c;它只有两个可能的取值&#xff1a;true&#xff08;真&#xff09;和 false&#xff08;假&#xff09;。bool 类型常用于条件判断和布尔运算中。 C 标准要求 bo…

Cooike Session

1 会话技术 1.1 会话管理概述 1.1.1 什么是会话 这里的会话&#xff0c;指的是web开发中的一次通话过程&#xff0c;当打开浏览器&#xff0c;访问网站地址后&#xff0c;会话开始&#xff0c;当关闭浏览器&#xff08;或者到了过期时间&#xff09;&#xff0c;会话结束。 …

UE5 录制透明png序列帧

以下是在 Unreal Engine 5 中录制透明 PNG 序列帧的详细步骤&#xff1a; 步骤1&#xff1a;创建一个场景 步骤2&#xff1a;打开序列录制器 在 Unreal Engine 5 中&#xff0c;首先需要打开序列录制器。你可以通过点击顶部菜单栏的 窗口 > 开发人员工具 > 序列录制器 …

HashMap、HashTable、ArrayList、定长数组之间的性能和速度比较

1、HashMap & HashTable 的速度谁快 HashMap 牺牲了线程安全提高了效率&#xff0c;HashTable 牺牲了效率换来了线程安全 2、HashMap 为什么线程不安全 HashMap 底层维护了一个数组&#xff0c;当多线程的时候对这个数组操作是不安全的。 3、ArrayList & HashMap 通…

React写法——使用js高阶函数实现多条件搜索功能

&#x1f642;博主&#xff1a;爱学习的Akali king &#x1f642;本文核心&#xff1a;React写法——使用js高阶函数实现多条件搜索功能 目录 思考一下代码是什么&#xff1f;你如何看待编程语言&#xff1f;用react写法来实现&#xff0c;思路步骤&#xff1a;第一步&#x…

动态规划 DP (三)

4.分割DP 对于分割类型题&#xff0c;动态规划的状态转移方程通常并不依赖相邻的位置&#xff0c;而是依赖于满足分割 条件的位置。 1&#xff09; 力扣https://leetcode.cn/problems/perfect-squares/举个例子对于8来说&#xff0c;要计算 完全平方数的最少数量 &#xff0c;…

剑指offer刷题笔记--Num21-30

1--调整数组顺序使奇数位于偶数前面&#xff08;21&#xff09; 主要思路&#xff1a; 双指针法&#xff0c;左指针从 0 开始遍历&#xff0c;直到遇到偶数&#xff0c;右指针从 len - 1 开始遍历&#xff0c;直到遇到奇数&#xff1b; 这时左指针指向偶数&#xff0c;右指针指…

kube-proxy源码阅读

kube-proxy源码阅读 通过阅读kube-proxy的源码可以将proxy的主要逻辑总结为下图所示&#xff1a; 首先顺着代码阅读到ProxyServer->Run()函数&#xff0c;这里是kube-proxy启动的主逻辑&#xff0c;启动了两个server&#xff0c;分别是&#xff1a; ...var errCh chan err…

基于深度学习的高精度安全帽背心检测识别系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于深度学习的高精度安全帽背心检测识别系统可用于日常生活中或野外来检测与定位安全帽背心目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的安全帽背心目标检测识别&#xff0c;另外支持结果可视化与图片或视频检测结果的导出。本系统采用…

【软考网络管理员】2023年软考网管初级常见知识考点(18)-安全协议SSL与PGP、数据加密技术

涉及知识点 安全套接层协议SSL详解&#xff0c;PGP协议是什么&#xff1f;数据加密技术有哪些&#xff1f;软考网络管理员常考知识点&#xff0c;软考网络管理员网络安全&#xff0c;网络管理员考点汇总。 原创于&#xff1a;CSDN博主-《拄杖盲学轻声码》&#xff0c;更多考点…

JUC之LockSupport和中断

文章目录 1 线程中断机制1.1 什么是线程中断机制1.2 三大中断方法1.3 如何中断运行中的线程1.3.1 通过volatile变量实现1.3.1 通过AtomicBoolean实现1.3.1 通过interrupt和isInterrupted api实现 2 LockSupport2.1 为什么会出现LockSupport2.2 两道面试题 参考材料 1 线程中断机…

案例合集 | 创新社会组织服务,打造“数字化商协会”

数字化建设方兴未艾数字化商会势在必行 近年来&#xff0c;国务院、发改委等机构陆续出台了一系列鼓励数字经济与产业数字化发展的规划指南&#xff0c;内容主要涵盖鼓励平台经济建设、促进供应链数字化转型与提升中小企业数字化渗透等方面&#xff0c;顶层政策引导并促进企业…

使用Gradio的BarPlot模块创建交互式柱状图

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

SQL和NoSQL数据库的便捷速查表

在项目成功的过程中选择合适的数据库非常重要。以下是关键要点的总结&#xff1a; •SQL数据库提供结构化数据存储、SQL支持和关系能力。•NoSQL数据库提供灵活性、可扩展性和分布式架构。•专门的数据库&#xff0c;如列存储、图形数据库、空间数据库和时间序列数据库&#xf…

Jetpack compose中实现流畅的Theme选择器动画

Jetpack compose中实现流畅的Theme Picker动画 Jetpack Compose改变了我们在Android上管理主题的方式。它提供了更大的灵活性&#xff0c;使我们能够以更多的方式定义用户界面&#xff08;UI&#xff09;。此外&#xff0c;Compose中的动画系统使我们能够轻松创建令人印象深刻和…

如何训练全自动的安卓ai脚本(yolov5 为例) 实现游戏应用自动屏幕识别点击

必要资源 yolo训练方法&#xff0c;调参要点:https://docs.ultralytics.com/ncnn yolov5 示例:https://github.com/nihui/ncnn-android-yolov5在线模型转换&#xff1a;https://convertmodel.com/ 硬件配备 32G 内存, 2060 英伟达显卡 操作步骤 1.准备好数据集&#xff0c…

管理类联考——逻辑——知识篇——形式逻辑——五、联言选言——haimian

联言&选言 考点分析 考点分析 削弱 年度 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023题量6222111 联言 本质定义 联言命题是断定两种或两种以上事物情况同时存在的命题&#xff0c;用“A并且B”表示&#xff0c;逻辑符号为A ∧ B。 若“A ∧ B”为真…

为什么uCOSii的栈顶不再是0x20000000

我将FreeRTOS的工程文件移植到基于uCOSii系统中&#xff0c;发现两个系统生成的栈顶地址不一样&#xff0c;即使栈的大小相同&#xff0c;都是用Keil编译器&#xff0c;差别很大。见下图&#xff1a; Stack_Size EQU 0x00001000; 以前一直使用FreeRTOS系统&#xff0c…