Java多线程编程——基础篇

目录

前言

一、进程与线程

1、进程

2、线程

二、并发与并行

1、并发

2、并行

三、线程调度

1、CPU时间片

2、调度方式

①时间片轮转

②抢占式调度

四、线程实现方式

1、继承 Thread 类

Thread的多种构造函数:

2、实现 Runnable 接口

五、线程的核心方法

1、start()

2、run()

3、isAlive()

4、sleep(long millis)

5、join()

6、interrupt()

7、currentThread()

六、线程状态


前言

        计算机早期程序是按顺序执行的

        一个任务完成后才开始下一个任务

        想象一下

        当你在网页中下载东西时

        整个页面就会冻结

        不能进行其他操作

        

        随着硬件性能提升和人们的需求增长

        大家开始思考:

        如何让多个任务同时进行

        这便催生了多线程编程的概念

        Java从诞生之初就将多线程作为核心特性之一

一、进程与线程

在介绍线程之前,我们先来学习一下进程,它们二者有着密不可分的联系:

1、进程

进程指一个内存中运行的应用程序,它是系统运行程序的基本单位

一个程序从创建、运行到消亡,这样整个过程就是一个进程

一个操作系统中可以同时运行多个进程,每个进程运行时,系统都会为其分配独立的内存空间

2、线程

线程是进程中的一个执行单元,负责当前进程中程序的执行

是操作系统能够进行运算调度的最小单位

总结一下:

        进程是资源分配的最小单位

        线程是CPU调度的最小单位        

        一个程序运行后至少有一个进程

        一个进程中至少包含一个线程(main线程)或多个线程

        当一个进程中启动了多个线程

        这个程序就是多线程程序

JVM是多线程的吗?

是的,JVM在运行程序的同时,进行GC垃圾回收

二、并发与并行

1、并发

指两个或多个事件 在同一时间段内 发生

线程的并发执行,是指在一个时间段内(微观,转瞬即逝)

两个或多个线程,使用同一个CPU交替运行

2、并行

指两个或多个事件 在同一时刻 发生(同时发生)

现成的并行执行,是指在同一时刻

两个或多个线程,各自使用一个CPU同时运行

        单核CPU计算机,同一时刻只能有一个线程

        多核CPU计算机,同一时刻可能有多个线程

        

计算机内有专门的资源调度算法

所以我们从程序层面无法得知也无法干涉具体用几个CPU运行

三、线程调度

针对于并发多线程(只有一个CPU讨论

1、CPU时间片

发明时间片机制的原因:

  • 为了防止一个线程一直占用CPU,保证系统响应速度
  • 实现多任务并发执行,让多个线程看起来是同时运行的
  • 通过时间片轮转调度算法实现公平公正的CPU资源分配

CPU时间片(Time Slice)

操作系统分配给每个线程或进程的CPU执行时间段

是现代操作系统实现多任务处理的核心机制

        

由于这个时间段一般是微秒纳秒级别

所以宏观上我们会感觉多个线程在同时运行代码

其实微观中它们是交替运行的

只不过交替的速度极快

2、调度方式

常见调度方式:

①时间片轮转

为每个就绪进程分配一个固定长度的时间片

当时间片用完时,即使进程尚未完成

也会被强制暂停并放到就绪队列末尾等待下一次调度

②抢占式调度

系统会让优先级高的线程优先使用CPU(提高抢占到的概率

如果优先级相同,随机选择一个线程获取CPU时间片

注意:

优先级高只是建议性,不是强制性

只是提高概率,不能确定实际结果

JVM中的线程为抢占式调度,可以调节优先级

四、线程实现方式

1、继承 Thread 类

java.lang.Thread

有两种实现办法:

  • 一种是新建一个类

步骤如下:

  1. 定义 Thread 的子类,重写 run() 方法,run()中的代码就是线程要执行的任务
  2. 创建 Thread 子类对象,该对象就代表一个要独立运行的线程
  3. 调用线程对象的 start() 方法来启动该线程
  • 一种是匿名内部类

步骤如下:

  1. 利用 Thread 的构造函数创建对象
  2. 调用该对象的 start() 方法来启动线程

这里就只展示匿名内部类的写法了(无参构造):

public class Test_Thread {public static void main(String[] args) {Thread t = new Thread() {// 重写run方法@Overridepublic void run() {System.out.println("in thread run...");//每隔1s输出一次for (int i = 0; i < 10; i++) {System.out.println("thread run ");//思考:为什么异常不能抛出?try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}};t.start();}
}

这里有个问题

为什么异常不能抛出,而是try-catch?

原因在于:

我们是在 重写 run() 方法

重写我们前面提到过,要保证与父类的一致性

父类中的 run() 并没有 throws

子类重写就不能擅自添加

Thread的多种构造函数:

1、无参构造器

public Thread() {init(null, null, "Thread-" + nextThreadNum(), 0);
}

线程名字默认是Thread-0Thread-1, ...(自动生成)

2、给线程自定义名字:

public Thread(String name) {init(null, null, name, 0);
}

3、把Runnable的对象当参数:

(Runnable的对象可能是实现类的、匿名内部类的等等,本身是接口不能实例化)

public Thread(Runnable target) {init(null, target, "Thread-" + nextThreadNum(), 0);
}

4、带线程组和Runnable的:

(线程组后面会提到)

public Thread(ThreadGroup group, Runnable target) {init(group, target, "Thread-" + nextThreadNum(), 0);
}

5、线程组和自定义名称:

public Thread(ThreadGroup group, String name) {init(group, null, name, 0);
}

6、Runnable 和 自定义名称:

public Thread(Runnable target, String name) {init(null, target, name, 0);
}

7、线程组、Runnable、自定义名称:

public Thread(ThreadGroup group, Runnable target, String name) {init(group, target, name, 0);
}

8、最完整的(包含栈大小的):

public Thread(ThreadGroup group, Runnable target, String name, long stackSize) {init(group, target, name, stackSize);
}

2、实现 Runnable 接口

java.lang.Runnable

该接口中只有一个抽象方法 run:

//JavaAPI-Runnable接口源码
public interface Runnable {public abstract void run();
}

其实 Thread 类也是 Runnable 接口的实现类:

//JavaAPI-Thread类源码分析
public class Thread implements Runnable {/* What will be run. */private Runnable target;public Thread() {//...}public Thread(Runnable target) {this.target = target;//..}@Overridepublic void run() {if (target != null) {target.run();}}
}

同样是两种实现:

  • 一种是在子类中实现 Runnable 接口
  • 另一种是匿名内部类
//1.创建Runnable实现类
class MyRunnable implements Runnable {//2.重写run方法@Overridepublic void run() {String name = Thread.currentThread().getName();for(int i = 20; i <= 70; i++)System.out.println("in thread: " + name + " i: " + i);}    
}public class Test08_Runnable {public static void main(String[] args) {//3.实例化对象Runnable r = new MyRunnable();//4.创建Thread对象Thread th = new Thread(r);th.setName("child-thread1");//5.启动线程th.start();//匿名内部类方式 获取Runnable实现类对象Runnable r2 = new Runnable() {@Overridepublic void run() {String name = Thread.currentThread().getName();for(int i = 80; i >= 30; i--){System.out.println("in thread: " + name + " i: " + i);}}};Thread th2 = new Thread(r2,"子线程2");th2.start();}
}

两种线程实现方式对比:

  • 继承 Thread 类

        好处:编程简单,可以直接使用 Thread 中的方法

        坏处:可扩展性差,浪费掉唯一的继承机会(java单继承)

  • 实现 Runnable 接口

        好处:扩展性强,实现该接口同时还可以继承其他类

        坏处:编程相对复杂,不能直接使用 Thread 类中的方法

3、实现 Callable 接口

使用不多,了解即可,较为繁琐

相关方法:

        V call() 

                计算结果,如果无法计算,则抛出一个异常

        FutureTask(Callable callable)

                创建一个 FutureTask

                一旦运行就执行给定的 Callable

        V get()

                如有必要

                等待计算完成,获取其结果

实现步骤:

  •         定义一个子类实现 Callable 接口
  •         在子类中重写 call() 方法
  •         创建子类对象
  •         创建 Future 的实现类的 FutureTask 对象,将子类对象作为参数传进去
  •         创建 Thread 对象,把 FutureTask 对象作为构造方法的参数
  •         启动线程
  •         调用 get方法,就可以获取线程结束之后的结果(可选)
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;//1.创建Callable的实现类
class MyCallable implements Callable<String> {//2.重写call方法@Overridepublic String call() throws Exception {for (int i = 0; i < 100; i++) {System.out.println("跟女孩表白" + i);}//返回值就表示线程运行完毕之后的结果return "答应";}
}public class Test20_Callable {public static void main(String[] args) throws Exception 
{//3.实例化Callable的实现类类对MyCallable mc = new MyCallable();//Thread t1 = new Thread(mc);//4.创建Future的实现类FutureTask对象,把MyCallable对象作为构造方法的参数FutureTask<String> ft = new FutureTask<>(mc);//5.创建Thread对象,并传递ft对象作为构造器参数Thread t1 = new Thread(ft);//String s = ft.get();//6.开启线程t1.start();//7.获取线程方法执行后返回的结果String s = ft.get();System.out.println(s);}
}

五、线程的核心方法

1、start()

调用者:Thread 对象

参数:

返回值:void

作用:

        启动线程,使其进入就绪状态

        JVM会自动调用 run()方法

注意事项:

        每个线程只能调用一次 start()

        重复调用会抛出异常IllegalThreadStateException

        start()不会立即执行线程代码

        只是通知 JVM 该线程可以运行了

2、run()

调用者:Thread 对象 或 Runnable 实现类

参数:

返回值:void

作用:

        定义线程执行的具体逻辑

注意事项:

        直接调用 run() 不会启动新线程,而是在当前线程中执行

        应该通过 start() 启动

start() 和 run() 区别:

start():

        需要手动调用的,用于启动线程

        会创建新线程,JVM负责自动调用该线程的 run()

        线程进入就绪状态

run():

        需要手动重写的,是线程要执行的任务

        手动调用的话就只是调用普通方法

        和调用 sayHello() 一样没区别

        不会创建新线程

        这样就背离了初衷

        被JVM调用

3、isAlive()

调用者:Thread 对象

参数:

返回值:boolean(true表示线程仍在运行,false表示线程已经结束)

作用:

        检查线程是否处于活动状态

注意事项:

        线程在新建状态和终止状态返回false

        线程在就绪、运行和阻塞状态返回true

      (这里涉及到的线程状态后面会提到)

4、sleep(long millis)

调用者:Thread 类(静态方法)

参数:millis(休眠的毫秒数)

返回值:void

作用:

        使当前正在执行的线程暂停执行指定的毫秒数

注意事项:

        是静态方法,作用于当前线程

        使用方式:Thread.sleep(毫秒数);

        不保证精确的休眠时间,取决于系统定时器和调度器的精度

        可能会抛出 InterruptedException,所以需要异常处理

public class SleepExample {public static void main(String[] args) {System.out.println("开始休眠: " + System.currentTimeMillis());try {Thread.sleep(2000); // 当前线程休眠2秒} catch (InterruptedException e) {System.out.println("休眠被中断");Thread.currentThread().interrupt(); // 恢复中断状态}System.out.println("休眠结束: " + System.currentTimeMillis());}
}

5、join()

调用者:Thread 对象

参数:

        ①无参版本:无限等待,直到调用者运行完毕

        join(long millis):等待指定毫秒数

        ③join(long millis, int nanos):等待指定毫秒数和纳秒数

返回值:void

作用:

        在当前线程中使用,当前线程停止运行

        等待 调用join的线程 运行完毕或运行指定毫秒数后再开始运行

注意事项:

        可能抛出异常 InterruptedException需要异常处理

        等待期间若其他线程中断了当前线程,会抛出异常

public class Test {public static void main(String[] args) {Thread t2 = new Thread(new Runnable(){@Overridepublic void run(){try {Thread.sleep(200);  //等待t1执行到i = 3} catch (InterruptedException e) {e.printStackTrace();}for (int i = 1; i <= 5; i++) {System.out.println(Thread.currentThread().getName() + "正在运行第" + i + "次");}}},"t2");Thread t1 = new Thread(new Runnable(){@Overridepublic void run(){for (int i = 1; i <= 5; i++) {if(i == 3){try {t2.join();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(Thread.currentThread().getName() + "正在运行第" + i + "次");}}},"t1");t1.start();t2.start();}
}

为了更好测试,刚开始不让 t1 和 t2 抢夺时间片,先让 t2 小睡一会

6、interrupt()

调用者:Thread 对象

参数:

返回值:void

作用:

        中断线程

注意事项:

        不会真正停止线程,只是设置中断状态

        如果线程处于阻塞状态(如 sleep、wait)

        会抛出异常 InterruptedException

public class InterruptExample {public static void main(String[] args) {Thread thread = new Thread(() -> {while (!Thread.currentThread().isInterrupted()) {System.out.println("线程正在运行...");try {Thread.sleep(1000);} catch (InterruptedException e) {System.out.println("线程被中断");// 注意:InterruptedException会清除中断状态Thread.currentThread().interrupt(); // 重新设置中断状态break;}}System.out.println("线程结束");});thread.start();try {Thread.sleep(3000); // 主线程等待3秒} catch (InterruptedException e) {e.printStackTrace();}thread.interrupt(); // 中断子线程}
}

7、currentThread()

调用者:Thread 类(静态方法)

参数:

返回值:当前正在执行的线程对象

作用:

        返回对当前正在执行的线程对象的引用

注意事项:

        常用于获取当前线程的信息或者设置线程属性

        以及使用 Thread 的静态方法

public class CurrentThreadExample {public static void main(String[] args) {Thread current = Thread.currentThread();System.out.println("当前线程名称: " + current.getName());System.out.println("当前线程ID: " + current.getId());System.out.println("当前线程优先级: " + current.getPriority());// 设置线程名称current.setName("主线程");System.out.println("修改后线程名称: " + current.getName());}
}

六、线程状态

  1. 新建状态(New):线程对象被创建,但还没有调用 start() 方法
  2. 就绪状态(Runnable):调用了 start() 方法,等待 CPU 调度
  3. 终止状态(Terminated):线程执行完毕 异常退出
  4. 阻塞状态(Blocked):由于某种原因放弃 CPU 使用权
  5. 等待状态(Waiting):线程在没有指定时间的情况下进入等待,必须等其他线程显示唤醒才能继续执行
  6. 超时等待(Timed Waiting):线程在指定时间内进入等待,时间到达后会自动苏醒,不需要其他线程显示唤醒

public class ThreadStateExample {public static void main(String[] args) throws InterruptedException {// NEW 新建状态Thread newThread = new Thread(() -> {System.out.println("线程正在运行");});System.out.println("NEW状态: " + newThread.getState()); // NEW// 启动线程进入 Runnable 可运行状态newThread.start();System.out.println("RUNNABLE状态: " + newThread.getState()); // RUNNABLE// Blocked 锁阻塞状态示例Object lock = new Object();Thread blockedThread = new Thread(() -> {synchronized (lock) {try {Thread.sleep(5000); // 持有锁5秒} catch (InterruptedException e) {e.printStackTrace();}}});Thread blockedThread2 = new Thread(() -> {synchronized (lock) { // 尝试获取已被占用的锁System.out.println("获取到锁");}});blockedThread.start();Thread.sleep(100); // 确保第一个线程先获取锁blockedThread2.start();Thread.sleep(100); // 等待第二个线程尝试获取锁System.out.println("BLOCKED状态: " + blockedThread2.getState()); // BLOCKED// Waiting 等待状态示例Object waitLock = new Object();Thread waitingThread = new Thread(() -> {synchronized (waitLock) {try {waitLock.wait(); // 进入等待状态} catch (InterruptedException e) {e.printStackTrace();}}});waitingThread.start();Thread.sleep(100);System.out.println("WAITING状态: " + waitingThread.getState()); // WAITING// Timed Waiting 超时等待状态示例Thread timedWaitingThread = new Thread(() -> {try {Thread.sleep(5000); // 进入超时等待状态} catch (InterruptedException e) {e.printStackTrace();}});timedWaitingThread.start();Thread.sleep(100);System.out.println("TIMED_WAITING状态: " + timedWaitingThread.getState()); // TIMED_WAITING// TERMINATED 终止状态示例Thread terminatedThread = new Thread(() -> {System.out.println("线程任务完成");});terminatedThread.start();terminatedThread.join(); // 等待线程执行完毕System.out.println("TERMINATED状态: " + terminatedThread.getState()); // TERMINATED// 唤醒等待线程synchronized (waitLock) {waitLock.notify();}}
}

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

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

相关文章

阿里云的centos8 服务器安装MySQL 8.0

在 CentOS 8 上安装 MySQL 8.0 可以通过添加 MySQL 官方 YUM 仓库并使用 dnf 命令安装。以下是具体步骤&#xff1a; 步骤如下&#xff1a; 下载并添加 MySQL 官方 YUM 仓库 运行以下命令下载 MySQL 8.0 的 YUM 仓库配置文件&#xff1a; sudo dnf install https://dev.mysql.…

【运维进阶】Linux 正则表达式

Linux 正则表达式定义&#xff1a;正则表达式是一种pattern&#xff08;模式&#xff09;&#xff0c;用于与待搜索字符串匹配&#xff0c;以查找一个或多个目标字符串。组成&#xff1a;自成体系&#xff0c;由两类字符构成普通字符&#xff1a;未被显式指定为元字符的所有可打…

STM32输入捕获相位差测量技术详解(基于TIM1复位模式)

本文将深入解析基于STM32定时器输入捕获功能的方波相位差测量技术&#xff0c;通过复位模式实现高精度相位检测。以下是完整的代码实现与详细原理分析。一、相位差测量原理相位差测量基于两个同频方波信号下降沿时间差计算。核心原理&#xff1a;​复位模式​&#xff1a;将TIM…

什么是股指期货可转移阿尔法策略?

阿尔法&#xff08;Alpha&#xff09;是投资领域的一个术语&#xff0c;用来衡量投资组合的超额收益。简单来说&#xff0c;阿尔法就是你在市场上赚的比平均水平多出来的那部分钱。比如&#xff0c;市场平均收益率是5%&#xff0c;但你的投资组合收益率是10%&#xff0c;那你的…

AXI GPIO S——ZYNQ学习笔记10

AXI GPIO 同意通道混合输入输出中断控制#KEY set_property IOSTANDARD LVCMOS18 [get_ports {AXI_GPIO_KEY_tri_io[0]}] set_property PACKAGE_PIN J13 [get_ports {AXI_GPIO_KEY_tri_io[0]}] set_property IOSTANDARD LVCMOS18 [get_ports {AXI_GPIO_KEY_tri_io[1]}] set_pro…

如何通过传感器选型优化,为设备寿命 “续航”?

在当今竞争激烈的工业领域&#xff0c;企业就像在一场没有硝烟的战争中角逐&#xff0c;设备便是企业的“秘密武器”。设备的使用寿命&#xff0c;如同武器的耐用程度&#xff0c;直接决定了企业在生产战场上的“战斗力”。延长设备寿命&#xff0c;已然成为众多企业降低生产成…

WebSocket连接的例子

// 初始化WebSocket连接 const initWebSocket () > {console.log("初始化链接中...")const websocketUrl ws://61.54.84.16:9090/;// WebSocket服务器地址websocket new WebSocket(websocketUrl)//使用真实的webscket// websocket new MockWebSocket(websocket…

c++之指针和引用

一 使用场景 C++ 什么时候使用指针?什么时候使用引用?什么时候应该按值传递?_引用什么时候用比较好-CSDN博客 只使用传递过来的值,而不对值进行修改 需要修改传递过来的值 内置数据类型 按值传递(小型结构) 指针传递 数组 指针传递 指针传递 结构 指针或引用(较大的结构…

pytorch学习笔记-模型训练、利用GPU加速训练(两种方法)、使用模型完成任务

应该算是完结啦~再次感谢土堆老师&#xff01; 模型训练 模型训练基本可以分为以下几个步骤按序执行&#xff1a; 引入数据集-使用dataloader加载数据集-建立模型-设置损失函数-设置优化器-进行训练-训练中计算损失&#xff0c;并使用优化器更新参数-模型测试-模型存储 习惯上会…

深度卷积神经网络AlexNet

在提出LeNet后卷积神经网络在计算机视觉和机器学习领域中报有名气&#xff0c;但是卷积神经网络并没有主导这些领域&#xff0c;因为LeNet在小数据集上取得了很好的效果&#xff0c;在更大&#xff0c;更真实的数据集上训练卷积神经网络的性能 和可行性有待研究&#xff0c;20世…

数据结构-HashSet

在 Java 编程的世界里&#xff0c;集合框架是极为重要的一部分&#xff0c;而 HashSet 作为 Set 接口的典型实现类&#xff0c;在处理不允许重复元素的场景中频繁亮相。今天&#xff0c;我们就一同深入探究 HashSet&#xff0c;梳理它的特点、常用方法&#xff0c;以及和其他相…

心意行药号 · 慈心方的八种用法

心意行药号 慈心方的八种用法慈心方是心意行药号589个珍贵秘方中的一个养生茶方&#xff0c;配伍比例科学严谨&#xff0c;君臣佐使堪称经典&#xff0c;自古就有“小小慈心方&#xff0c;转动大乾坤”之说。自清代光绪年间传承至今&#xff0c;慈心方受益者逾百万计&#xff…

Spring面试宝典:Spring IOC的执行流程解析

在准备Spring框架的面试时&#xff0c;“Spring IOC的工作流程是什么&#xff1f;” 是一个非常经典的问题。虽然网上有很多详细的教程&#xff0c;但它们往往过于复杂&#xff0c;对于没有深入研究过源码的人来说理解起来确实有些困难。今天我们就来简化这个概念&#xff0c;从…

学习日志39 python

1 fromkeys()函数是什么在 Python 中&#xff0c;fromkeys() 是字典&#xff08;dict&#xff09;的一个类方法&#xff0c;用于创建一个新字典。它的作用是&#xff1a;根据指定的可迭代对象&#xff08;如列表、元组等&#xff09;中的元素作为键&#xff08;key&#xff09;…

SpringBoot + MyBatis-Plus 使用 listObjs 报 ClassCastException 的原因与解决办法

在项目中我们经常会遇到这种需求&#xff1a; 根据一组 ID 查询数据库&#xff0c;并返回指定字段列表。 我在写代码的时候&#xff0c;遇到了一个典型的坑&#xff0c;分享出来给大家。一、问题背景我的代码是这样写的&#xff08;查询项目表的负责人信息&#xff09;&#xf…

WT2606B 驱屏语音芯片新增蓝牙功能:功能集成一体化,产品升级自动化,语音交互无线化,场景应用普适化!

小伙伴们&#xff0c;欢迎来到我们的 &#xff03;唯创芯片小讲堂&#xff01;今天我们要为大家介绍一位多才多艺的"芯片全能手"——WT2606B驱屏语音芯片。这颗芯片将在今年8月的I0TE物联网展及ELEXCON 2025深圳国际电子展上大放异彩。在智能设备满天飞的今天&#x…

ORA-16331: container is not open ORA-06512: at “SYS.DBMS_LOGMNR“

使用Flink CDC、Debezium等CDC工具对Oracle进行基于log的实时数据同步时遇到异常ORA-16331: container is not open的解决方案。 1. 异常信息 异常信息通常如下&#xff1a; at oracle.jdbc.driver.OracleStatement.executeInternal(OracleStatement.java:1823) at oracle.jdbc…

「三维共振」:重构实体零售的破局模式

在电商冲击与消费升级的双重浪潮下&#xff0c;传统零售模式正面临前所未有的挑战。wo店首创的 “三维共振” 运营模式&#xff0c;以场景体验为根基、数据驱动为引擎、社群共生为纽带&#xff0c;构建起线上线下深度融合的新型零售生态&#xff0c;至今已实现连续 18 个月客流…

将集合拆分成若干个batch,并将batch存于新的集合

在使用saveAll()等方法时&#xff0c;为了防止集合元素过大&#xff0c;使用splitList将原集合&#xff0c;分割成若干个小集合 import java.util.ArrayList; import java.util.List;public class ListUtils {/*** 将集合拆分成若干个batch,并将batch存于新的集合** param list…

Java主流框架全解析:从企业级开发到云原生

Java作为一门历史悠久且应用广泛的编程语言&#xff0c;其强大的生态系统离不开各种优秀的框架支持。无论是传统的企业级应用开发&#xff0c;还是现代的微服务、云原生架构&#xff0c;Java都提供了丰富的框架选择。本文将全面解析当前主流的Java框架&#xff0c;涵盖Web开发、…