Java多线程基础:进程、线程与线程安全实战

🚀 极客小贴士

💡 你知道吗? 在Java中,每个线程都有自己的栈空间,但共享堆内存。这就像每个员工都有自己的办公桌,但共享公司的会议室和打印机!


🎯 项目实战案例:电商订单处理系统

在开始学习多线程之前,让我们先看一个真实的项目案例,了解多线程在实际开发中的重要性。

📱 项目背景

假设你正在开发一个电商订单处理系统,系统需要处理多个用户同时下单的请求:

用户下单 → 库存检查 → 支付验证 → 订单生成 → 库存扣减 → 物流通知

🐌 单线程版本的问题

如果使用单线程处理,多个用户的订单需要排队等待,一个接一个处理:

用户A的订单: 开始处理 → 库存检查(200ms) → 支付验证(300ms) → 订单生成(100ms) → 库存扣减(150ms) → 物流通知(200ms) → 完成(950ms)
用户B的订单: 等待... → 等待... → 等待... → 等待... → 等待... → 等待... → 开始处理 → 完成(1900ms)
用户C的订单: 等待... → 等待... → 等待... → 等待... → 等待... → 等待... → 等待... → 开始处理 → 完成(2850ms)

问题分析

  • 用户B的订单需要等待950ms
  • 用户C的订单需要等待1900ms
  • 用户体验极差,容易超时
  • 系统吞吐量低

🚀 多线程版本的优势

使用多线程并行处理,多个用户的订单可以同时进行,互不阻塞:

用户A的订单: 开始处理 → 库存检查(200ms) → 支付验证(300ms) → 订单生成(100ms) → 库存扣减(150ms) → 物流通知(200ms) → 完成(950ms)
用户B的订单: 开始处理 → 库存检查(200ms) → 支付验证(300ms) → 订单生成(100ms) → 库存扣减(150ms) → 物流通知(200ms) → 完成(950ms)
用户C的订单: 开始处理 → 库存检查(200ms) → 支付验证(300ms) → 订单生成(100ms) → 库存扣减(150ms) → 物流通知(200ms) → 完成(950ms)

优势分析

  • 三个订单同时处理,总耗时约950ms
  • 相比单线程版本,性能提升3倍
  • 用户体验大幅改善
  • 系统吞吐量显著提升

🔧 技术实现要点

在这个项目中,多线程技术解决了以下关键问题:

  1. 并发处理:多个订单同时处理,提高系统响应速度
  2. 资源竞争:库存数据需要线程安全访问
  3. 异步通知:物流通知可以异步发送,不阻塞主流程
  4. 性能优化:通过线程池管理线程,避免频繁创建销毁

🎭 项目小贴士:这个案例展示了多线程在真实项目中的价值。不是所有的"慢"都能通过多线程解决,但多线程确实能解决很多性能瓶颈问题!


🎯 为什么需要学习多线程?

在开始之前,让我们先思考一个问题:为什么现代Java开发必须掌握多线程?

通过上面的电商订单处理系统案例,我们已经看到了多线程的巨大价值:

  • 性能提升:从单线程的2850ms优化到多线程的950ms,性能提升3倍
  • 用户体验:响应时间从秒级优化到毫秒级
  • 系统吞吐量:同时处理多个订单,大幅提升系统处理能力

这就是多线程的魅力:让程序跑得更快,用户体验更好!

🎭 生活小剧场:想象一下,如果餐厅只有一个服务员,所有顾客都要排队点餐、等菜、结账,那得多慢啊!多线程就像多个服务员同时服务不同顾客,效率瞬间提升!

🔍 多线程的应用场景

多线程技术主要解决以下问题:

  1. 提高系统响应性:避免长时间操作阻塞用户界面
  2. 提升系统吞吐量:同时处理多个请求,提高资源利用率
  3. 实现异步处理:将耗时操作放到后台,不阻塞主流程
  4. 充分利用多核CPU:现代CPU多核心,多线程能发挥硬件优势

⚠️ 多线程的适用性

需要注意的是,多线程不是万能的

  • 适合场景:CPU密集型任务、IO密集型任务、需要并发处理的业务
  • 不适合场景:简单的顺序处理、单线程就能满足性能要求的场景
  • 使用原则:在需要的时候使用,不要为了多线程而多线程

🔍 进程 vs 线程:从操作系统角度理解

什么是进程?

进程(Process) 是操作系统分配资源的基本单位。每个进程都有自己独立的内存空间,包括:

  • 代码段
  • 数据段
  • 堆内存
  • 栈内存

什么是线程?

线程(Thread) 是进程内的执行单元,是CPU调度的基本单位。多个线程共享同一个进程的资源。

生动的比喻

想象一下:

  • 进程就像一家公司
  • 线程就像公司里的员工
  • 内存空间就像公司的办公大楼
  • CPU就像公司的老板,负责给员工分配任务

关键区别

  • 进程间相互独立,一个进程崩溃不会影响其他进程
  • 线程间共享资源,一个线程崩溃可能影响整个进程

🏢 公司管理小贴士:如果公司A倒闭了,公司B不会受影响。但如果公司A里的某个员工离职了,可能会影响其他同事的工作。这就是进程和线程的区别!


🚀 线程的创建方式:三种方法详解

Java中创建线程有三种主要方式,让我们逐一了解:

方式1:继承Thread类

这是最直接的方式,但不推荐,因为Java只支持单继承。

public class MyThread extends Thread {@Overridepublic void run() {System.out.println("线程 " + Thread.currentThread().getName() + " 正在执行");}
}// 使用方式
MyThread thread = new MyThread();
thread.start();

为什么不推荐?

  • 继承Thread类后,无法继承其他类
  • 违反了"组合优于继承"的设计原则

⚠️ 设计原则提醒:就像你不能同时是"程序员"和"设计师"(假设这是互斥的),Java类也不能同时继承多个类。这就是为什么推荐使用接口!

方式2:实现Runnable接口(推荐)

这是最推荐的方式,符合面向接口编程的原则。

public class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println("线程 " + Thread.currentThread().getName() + " 正在执行");}
}// 使用方式
Thread thread = new Thread(new MyRunnable());
thread.start();

优势

  • 可以继承其他类
  • 可以传入参数
  • 更灵活,符合面向接口编程

🎯 最佳实践:这就像你可以同时拥有"程序员"和"健身教练"的身份,互不冲突!

方式3:使用Lambda表达式(最简洁)

Java 8引入Lambda表达式后,创建线程变得非常简洁:

// 使用Lambda表达式
Thread thread = new Thread(() -> {System.out.println("线程 " + Thread.currentThread().getName() + " 正在执行");
});
thread.start();// 更简洁的写法
new Thread(() -> {System.out.println("线程 " + Thread.currentThread().getName() + " 正在执行");
}).start();

优势

  • 代码简洁
  • 适合简单的线程任务
  • 可读性好

🚀 极客小技巧:Lambda表达式让代码变得像写诗一样优雅!() -> {} 就是现代Java的诗歌!


📊 线程的生命周期:6种状态详解

Java线程有6种状态,让我们通过一个生动的例子来理解:

状态转换图

NEW → RUNNABLE → RUNNING → BLOCKED/WAITING/TIMED_WAITING → TERMINATED

🎭 状态转换小剧场:这就像一个人的一生:出生(NEW) → 准备上学(RUNNABLE) → 正在学习(RUNNING) → 遇到困难(BLOCKED) →
等待帮助(WAITING) → 完成学业(TERMINATED)

🔄 线程状态转换详细流程图

                    ┌─────────────────┐│      NEW        │ ← 创建线程对象│   (新建状态)     │└─────────┬───────┘││ thread.start()▼┌─────────────────┐│    RUNNABLE     │ ← 等待CPU分配时间片│  (可运行状态)    │└─────────┬───────┘││ CPU分配时间片▼┌─────────────────┐│     RUNNING     │ ← 正在执行run()方法│   (运行状态)     │└─────────┬───────┘││ 遇到synchronized│ 或等待资源▼┌─────────────────────┼─────────────────────┐│                     │                     │▼                     ▼                     ▼
┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│   BLOCKED   │    │   WAITING   │    │TIMED_WAITING│
│  (阻塞状态)  │    │  (等待状态)  │    │(限时等待状态)│
│             │    │             │    │             │
│ 等待获取锁   │    │ 等待通知     │    │ 等待超时     │
│             │    │             │    │             │
│             │    │             │    │             │
└─────────┬───┘    └──────┬──────┘    └──────┬──────┘│               │                  ││               │                  ││               │                  │└───────────────┼──────────────────┘││ 被唤醒/超时/获取锁▼┌─────────────────┐│    RUNNABLE     │ ← 重新进入可运行状态│  (可运行状态)    │└─────────┬───────┘││ run()方法执行完毕▼┌─────────────────┐│   TERMINATED    │ ← 线程结束│   (终止状态)     │└─────────────────┘状态转换触发条件:
┌─────────────┬─────────────────────────────────────────────────┐
│    状态     │                  触发条件                        │
├─────────────┼─────────────────────────────────────────────────┤
│ NEW         │ new Thread() 创建线程对象                        │
│ RUNNABLE    │ thread.start() 启动线程                         │
│ RUNNING     │ CPU分配时间片,开始执行                          │
│ BLOCKED     │ 等待获取synchronized锁                          │
│ WAITING     │ Object.wait(), Thread.join()                    │
│ TIMED_WAITING│ Thread.sleep(), Object.wait(timeout)           │
│ TERMINATED  │ run()方法执行完毕或异常退出                      │
└─────────────┴─────────────────────────────────────────────────┘

🔄 流程图说明:这个流程图展示了Java线程的完整生命周期。就像人生一样,线程也会经历不同的阶段,每个阶段都有特定的触发条件和转换路径。理解这些状态转换对于调试多线程程序非常重要!

详细状态说明

1. NEW(新建状态)

线程被创建但还没有调用start()方法。

Thread thread = new Thread(() -> {// 线程任务
});
// 此时线程处于NEW状态

👶 生活比喻:就像刚出生的婴儿,已经存在但还没有开始活动

2. RUNNABLE(可运行状态)

线程调用了start()方法,等待CPU分配时间片。

thread.start(); // 线程进入RUNNABLE状态

🏃 生活比喻:就像运动员站在起跑线上,等待发令枪响

3. RUNNING(运行状态)

线程正在执行run()方法中的代码。

🏃‍♂️ 生活比喻:就像运动员正在赛道上奔跑

4. BLOCKED(阻塞状态)

线程等待获取锁,无法进入同步代码块。

synchronized (lock) {// 其他线程等待获取锁时,处于BLOCKED状态
}

🚪 生活比喻:就像在等厕所,前面有人在使用,你只能排队等待

5. WAITING(等待状态)

线程调用了wait()、join()等方法,等待其他线程通知。

synchronized (lock) {lock.wait(); // 线程进入WAITING状态
}

生活比喻:就像在等外卖,不知道什么时候能到,只能耐心等待

6. TIMED_WAITING(限时等待状态)

线程调用了sleep()、wait(timeout)等方法,等待指定时间。

Thread.sleep(1000); // 线程进入TIMED_WAITING状态,等待1秒

⏱️ 生活比喻:就像设置了一个闹钟,知道什么时候会响

7. TERMINATED(终止状态)

线程执行完毕或异常退出。

🏁 生活比喻:就像完成了任务,可以休息了


🔒 线程安全:三大特性深度解析

什么是线程安全?

线程安全是指多个线程同时访问共享数据时,程序能够正确执行,不会出现数据不一致的问题。

🎭 生活小剧场:想象一下,如果银行系统不是线程安全的,两个客户同时取钱,可能会出现"双倍取款"
的bug!这就像两个服务员同时记录同一张桌子的订单,结果记重了!

线程安全的三大特性

1. 原子性(Atomicity)

一个操作要么全部执行,要么全部不执行,不会被中断。

问题示例

public class Counter {private int count = 0;public void increment() {count++; // 这个操作不是原子的!}public int getCount() {return count;}
}

为什么count++不是原子的? count++实际上包含三个操作:

  1. 读取count的值
  2. 将count加1
  3. 将结果写回count

如果两个线程同时执行,可能出现:

  • 线程A读取count=0
  • 线程B读取count=0
  • 线程A计算0+1=1,写回count=1
  • 线程B计算0+1=1,写回count=1

最终结果应该是2,但实际是1!

🎭 生活小剧场:这就像两个人在同一个计数器上按按钮,如果按得太快,可能会漏掉一次计数!

2. 可见性(Visibility)

一个线程对共享变量的修改,其他线程能够立即看到。

问题示例

public class VisibilityDemo {private boolean flag = false;public void setFlag() {flag = true;}public boolean getFlag() {return flag;}
}

为什么会有可见性问题? 现代CPU有多级缓存,线程可能从不同的缓存读取数据,导致数据不一致。

🎭 生活小剧场:这就像公司有两个公告栏,一个在1楼,一个在2楼。如果只在1楼贴了通知,2楼的员工就看不到!

3. 有序性(Ordering)

程序执行的顺序与代码编写的顺序一致。

问题示例

int a = 1;
int b = 2;
int c = a + b;

为什么会有有序性问题? JVM和CPU可能对指令进行重排序优化,只要不影响单线程的执行结果。

🎭 生活小剧场:这就像厨师做菜,虽然菜谱上写的是"先放盐再放糖",但厨师可能会同时准备,只要最终味道对就行!


🛠️ 实战:线程安全的计数器实现

现在让我们动手实现一个线程安全的计数器,体验多线程编程的魅力!

🎯 实战小贴士:理论学得再好,不写代码都是纸上谈兵!让我们一起来写一个真正能跑的计数器!

问题分析

我们需要实现一个计数器,支持:

  • 增加计数
  • 减少计数
  • 获取当前值
  • 多个线程同时操作时保持数据一致性

🎭 生活小剧场:这就像银行柜台,多个客户同时存取款,但银行系统必须保证账目准确,不能出错!

方案1:使用synchronized关键字

public class SynchronizedCounter {private int count = 0;// 增加计数public synchronized void increment() {count++;}// 减少计数public synchronized void decrement() {count--;}// 获取当前值public synchronized int getCount() {return count;}
}

synchronized的作用

  • 确保同一时刻只有一个线程能执行被修饰的方法
  • 保证原子性、可见性和有序性
  • 自动获取和释放锁

🔒 锁的比喻:synchronized就像一个"请勿打扰"的牌子,当一个线程在工作时,其他线程必须等待!

方案2:使用ReentrantLock

import java.util.concurrent.locks.ReentrantLock;public class LockCounter {private int count = 0;private final ReentrantLock lock = new ReentrantLock();// 增加计数public void increment() {lock.lock();try {count++;} finally {lock.unlock(); // 确保锁被释放}}// 减少计数public void decrement() {lock.lock();try {count--;} finally {lock.unlock();}}// 获取当前值public int getCount() {lock.lock();try {return count;} finally {lock.unlock();}}
}

ReentrantLock的优势

  • 更灵活的锁控制
  • 支持公平锁和非公平锁
  • 可以响应中断
  • 支持超时获取锁

🔑 手动锁的比喻:ReentrantLock就像手动开关的门,你可以控制什么时候开门,什么时候关门,但必须记得关门!

方案3:使用原子类(推荐)

import java.util.concurrent.atomic.AtomicInteger;public class AtomicCounter {private final AtomicInteger count = new AtomicInteger(0);// 增加计数public void increment() {count.incrementAndGet();}// 减少计数public void decrement() {count.decrementAndGet();}// 获取当前值public int getCount() {return count.get();}
}

AtomicInteger的优势

  • 使用CAS(Compare-And-Swap)操作,性能更高
  • 无需加锁,无阻塞
  • 线程安全,性能优秀

原子操作的比喻:AtomicInteger就像原子弹一样,要么完全成功,要么完全失败,没有中间状态!

测试代码

让我们编写测试代码来验证这三种方案的线程安全性:

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class CounterTest {public static void main(String[] args) throws InterruptedException {// 测试synchronized计数器testCounter(new SynchronizedCounter(), "SynchronizedCounter");// 测试Lock计数器testCounter(new LockCounter(), "LockCounter");// 测试Atomic计数器testCounter(new AtomicCounter(), "AtomicCounter");}private static void testCounter(Object counter, String name) throws InterruptedException {int threadCount = 100;int operationsPerThread = 1000;ExecutorService executor = Executors.newFixedThreadPool(threadCount);CountDownLatch latch = new CountDownLatch(threadCount);long startTime = System.currentTimeMillis();for (int i = 0; i < threadCount; i++) {executor.submit(() -> {try {for (int j = 0; j < operationsPerThread; j++) {if (counter instanceof SynchronizedCounter) {SynchronizedCounter c = (SynchronizedCounter) counter;c.increment();} else if (counter instanceof LockCounter) {LockCounter c = (LockCounter) counter;c.increment();} else if (counter instanceof AtomicCounter) {AtomicCounter c = (AtomicCounter) counter;c.increment();}}} finally {latch.countDown();}});}latch.await();executor.shutdown();long endTime = System.currentTimeMillis();// 获取最终结果int finalCount = 0;if (counter instanceof SynchronizedCounter) {finalCount = ((SynchronizedCounter) counter).getCount();} else if (counter instanceof LockCounter) {finalCount = ((LockCounter) counter).getCount();} else if (counter instanceof AtomicCounter) {finalCount = ((AtomicCounter) counter).getCount();}System.out.println(name + " 测试结果:");System.out.println("  期望值: " + (threadCount * operationsPerThread));System.out.println("  实际值: " + finalCount);System.out.println("  耗时: " + (endTime - startTime) + "ms");System.out.println("  线程安全: " + (finalCount == threadCount * operationsPerThread ? "✅" : "❌"));System.out.println();}
}

🧪 测试小贴士:这个测试就像让100个人同时按1000次计数器,看看最终结果对不对!如果结果不对,说明我们的计数器有问题!

性能对比

  • AtomicCounter:性能最好,无锁操作,适合高并发场景
  • LockCounter:性能中等,显式锁控制,适合需要特殊锁功能的场景
  • SynchronizedCounter:性能稍慢,但使用简单,适合简单同步场景

🏆 性能排行榜:AtomicCounter就像短跑冠军,LockCounter像中长跑选手,SynchronizedCounter像马拉松选手!

📊 性能对比详细分析

计数器类型平均耗时(ms)吞吐量(ops/s)内存占用推荐指数适用场景
AtomicCounter4522,222⭐⭐⭐⭐⭐高并发、简单计数
LockCounter7812,821⭐⭐⭐⭐需要锁功能、中等并发
SynchronizedCounter9510,526⭐⭐⭐简单同步、低并发

特性分析

  • AtomicCounter: 无锁设计,性能最优,但功能相对简单
  • LockCounter: 功能丰富,支持公平锁、可中断、超时等特性
  • SynchronizedCounter: 使用简单,JVM自动优化,但功能有限

选择建议

  • 优先选择AtomicCounter: 适用于大多数计数场景
  • 选择LockCounter: 需要特殊锁功能时使用
  • 选择SynchronizedCounter: 简单同步场景,代码可读性要求高

🎯 今日总结

今天我们学习了:

📚 理论知识

  1. 进程与线程的区别:进程是资源分配单位,线程是执行单位
  2. 线程创建方式:继承Thread、实现Runnable、使用Lambda表达式
  3. 线程生命周期:6种状态的转换关系
  4. 线程安全三大特性:原子性、可见性、有序性

🛠️ 实战技能

  1. 三种线程安全计数器实现:synchronized、ReentrantLock、AtomicInteger
  2. 性能测试方法:多线程并发测试,验证线程安全性
  3. 最佳实践选择:根据具体场景选择合适的同步方案

💡 关键要点

  • 优先使用Atomic类:适用于大多数计数场景,性能最优
  • 合理使用synchronized:简单同步场景,代码清晰易维护
  • 灵活使用ReentrantLock:需要特殊锁功能时使用
  • 多线程测试很重要:理论正确不代表实际安全,必须进行并发测试
  • 选择合适的技术:根据业务场景、性能要求、维护成本综合考虑

⚠️ 注意事项

  • 多线程不是万能的:在不需要的场景下使用会增加系统复杂度
  • 性能测试要全面:不仅要测试功能正确性,还要测试性能表现
  • 考虑维护成本:选择技术方案时要考虑团队的技术水平和维护难度

📖 延伸阅读

📚 官方文档与规范

  • Oracle Java官方文档 - 并发编程 - Java并发编程的权威指南
  • Java并发编程实战 - JSR 166 - Java并发工具包的设计规范
  • OpenJDK源码 - 深入理解Java并发实现的底层原理

🎓 经典书籍推荐

  • 《Java并发编程实战》 - Brian Goetz等著,Java并发编程的圣经
  • 《深入理解Java虚拟机》 - 周志明著,JVM内存模型与线程安全详解
  • 《Effective Java》 - Joshua Bloch著,Java最佳实践中的并发编程部分
  • 《Java多线程编程核心技术》 - 高洪岩著,国内多线程编程经典教材

🔗 相关技术文章

  • Java内存模型(JMM)详解 - 理解线程安全的理论基础
  • Java并发工具包详解 -
    高级并发编程工具
  • 线程池最佳实践 -
    生产环境线程池配置指南

🛠️ 实用工具与框架

  • JProfiler - Java性能分析工具,线程状态监控
  • VisualVM - JDK自带的性能分析工具
  • Arthas - 阿里巴巴开源的Java诊断工具
  • JStack - JDK自带的线程堆栈分析工具

🔍 技术深度解析:同步处理对多用户的影响

常见疑问:Controller接口是多线程的吗?

这是一个容易混淆的概念,让我们来澄清一下:

误解:Controller接口本身是多线程的

Controller接口本身不是多线程的! 每个请求在Controller方法中都是单线程执行的。

真相:Web容器是多线程的,Controller是单线程的
架构层次:
┌─────────────────────────────────────────────────────────┐
│                    Web容器 (Tomcat/Netty)               │
│                    多线程处理请求                        │
├─────────────────────────────────────────────────────────┤
│  请求A → 分配线程1 → 执行Controller方法 → 返回结果      │
│  请求B → 分配线程2 → 执行Controller方法 → 返回结果      │
│  请求C → 分配线程3 → 执行Controller方法 → 返回结果      │
└─────────────────────────────────────────────────────────┘

🚨 同步处理的影响分析

影响程度取决于以下因素
  1. 并发量:同时请求的用户数量
  2. 线程池大小:Web容器可用的线程数量
  3. 单个请求处理时间:每个请求的耗时
  4. 系统负载:当前系统的繁忙程度
实际影响场景
并发场景同步处理影响说明
低并发 (QPS < 100)几乎无影响线程池充足,请求响应正常
中等并发 (QPS 100-1000)轻微影响响应时间增加,但系统稳定
高并发 (QPS 1000-5000)明显影响请求排队,可能出现超时
超高并发 (QPS > 5000)严重影响线程池耗尽,系统不可用

🛠️ 异步处理解决方案

方案1:CompletableFuture异步处理
@PostMapping("/order/create")
public CompletableFuture<Result> createOrder(@RequestBody OrderRequest request) {// 立即释放Web容器线程,异步处理订单return CompletableFuture.supplyAsync(() -> {return orderService.createOrder(request);});
}

优势

  • Web容器线程立即释放,可以处理其他请求
  • 系统吞吐量显著提升
方案2:消息队列异步处理
@PostMapping("/order/create")
public Result createOrder(@RequestBody OrderRequest request) {// 立即返回,订单异步处理String orderId = generateOrderId();orderMessageProducer.sendOrderMessage(request, orderId);return Result.success(orderId);
}

优势

  • 接口响应极快(<50ms)
  • 完全解耦,系统更稳定

📊 性能对比分析

处理方式响应时间吞吐量资源占用适用场景
同步处理订单处理时间占用Web容器线程低并发、简单业务
异步处理<100ms占用工作线程高并发、复杂业务
消息队列<50ms最高最小超高并发、异步业务

🎯 最佳实践建议

什么情况下使用同步处理
  • 低并发场景(QPS < 100)
  • 短耗时操作(< 500ms)
  • 简单业务逻辑,无复杂计算
什么情况下使用异步处理
  • 高并发场景(QPS > 1000)
  • 长耗时操作(> 3秒)
  • 资源密集型操作(CPU/内存/IO密集)
  • 用户体验要求高的场景

💡 关键理解点

  1. Controller接口本身是单线程的 - 每个请求在一个线程中执行
  2. Web容器是多线程的 - 多个请求可以同时被不同线程处理
  3. 同步处理会占用Web容器线程 - 导致线程池资源紧张
  4. 异步处理释放Web容器线程 - 提升系统整体吞吐量

总结:同步处理是否影响其他用户,取决于具体的并发场景和系统配置。在高并发场景下,异步处理是提升系统性能的必要手段。

🎉 结语

多线程编程是Java开发中的核心技能,掌握它不仅能让你的程序跑得更快,更能体现你的技术深度。希望这篇文章能帮助你在多线程编程的道路上走得更远!

记住:多线程编程就像学习骑自行车,一开始可能会摔倒,但只要坚持练习,最终就能熟练驾驭,享受并发编程带来的乐趣!

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

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

相关文章

2025 实测有效!手把手教你如何用实例代码(Python、JavaScript 、JAVA) 等实战代码,免费股票数据接口大全

​ 近年来&#xff0c;股票量化分析凭借其科学性与系统性&#xff0c;逐渐走进大众视野并受到广泛关注。对于这一领域的初学者而言&#xff0c;入门路上的第一道关卡便是如何获取全面且精准的股票数据。要知道&#xff0c;实时交易数据、历史交易记录、财务数据以及基本面信息等…

KMP 算法相关练习题

大家好&#xff0c;今天是2025年8月31日&#xff0c;上一期我给大家分享了 KMP 算法的相关知识&#xff0c;今天我来带领大家学习4道 KMP 相关的算法题。 在学习算法题之前&#xff0c;还是希望大家能够要先学会 KMP 算法&#xff08;可以参考这篇文章&#xff1a;KMP 算法&am…

张柏芝亮相林家谦演唱会 再次演绎《任何天气》

近日&#xff0c;张柏芝作为特别嘉宾亮相歌手林家谦演唱会。当天&#xff0c;张柏芝身着一袭浅米色蕾丝裙装&#xff0c;轻盈面料搭配层叠设计&#xff0c;行走间裙摆微扬&#xff0c;温柔气质满溢&#xff0c;为舞台增添了一抹温柔亮色。舞台上&#xff0c;张柏芝接连演绎《任…

Android 权限申请现代化指南

Android 权限申请现代化指南 一、核心概念&#xff1a;权限分类 Android 将权限分为三大类&#xff0c;申请方式各不相同&#xff1a; 普通权限 (Normal Permissions)范围&#xff1a;涉及应用沙盒外部但对用户隐私或设备操作风险极低的操作。示例&#xff1a;网络访问 (IN…

大话 IOT 技术(3) -- MQTT篇

文章目录前言前情提要MQTT介绍组成万恶的appmqtt服务端伪代码实现开源的力量后话当你迷茫的时候&#xff0c;请点击 物联网目录大纲 快速查看前面的技术文章&#xff0c;相信你总能找到前行的方向 前言 本篇将开始讲述IOT技术的一个重点&#xff0c;mqtt协议。 我发现有一个…

大语言模型生成的“超龄劳动者权益保障制度系统化完善建议(修订版)”

大纲 │ ├── 一、基于征求意见稿现状的评估 │ ├── 制度意义&#xff1a;25条暂行规定首次明确权益范围&#xff0c;提供法律依据 │ └── 关键缺陷 │ ├── 法律定位不明确 │ ├── 社保衔接不足 │ └── 实施机制不完善 │ ├── 二、法…

【UnityAS】Unity Android Studio 联合开发快速入门:环境配置、AAR 集成与双向调用教程

这是一篇2021年的存档&#xff0c;使用Unity2020版本。 至今&#xff0c;Unity与AS很多通讯方式也是基于此衍生。 作为Unity与AS联合开发的受益者&#xff0c;难得掏出自己的饭碗&#xff0c;诸君共享&#xff01; Unity & Android Studio 联合开发快速入门 ——Unity与AS…

前后端联合实现多个文件上传

1、前端 Vue3CommonApplyBasicInfoForm.vue<script setup lang"ts" name"CommonApplyBasicInfoForm"> ...... // 文件输入实例对象 const fileInputRef ref<HTMLInputElement | null>(null); // 选择文件列表 const selectedFiles ref<Fi…

软考高级--系统架构设计师--综合知识真题解析

系列文章目录 文章目录系列文章目录一、2019年真题二、2020年真题三、2021年真题四、2022年真题总结一、2019年真题 二、2020年真题 三、2021年真题 四、2022年真题 总结

“帕萨特B5钳盘式制动器结构设计三维PROE模型7张CAD图纸PDF图“

摘 要本文首先对汽车制动器原理和对各种各样的制动器进行分析,详细地阐述了各类制动器的结构,工作原理和优缺点。再根据轿车的车型和结构选择了适合的方案。根据市场上同系列车型的车大多数是滑钳盘式制动器,而且滑动钳式盘式制动器结构简单,性能居中,设计规范,所以我选择滑动…

SQL注入6----(其他注入手法)

一.前言 本章节来介绍一下其他的注入手法&#xff0c;也就是非常规注入手法&#xff0c;来和大家介绍一下 二.加密注入 前端提交的有些数据是加密之后&#xff0c;到了后台在解密&#xff0c;然后再进行数据库查询等相关操作的&#xff0c;那么既然如 此我们也应该将注入语句…

visual studio2022 配置 PCL 1.13.1

PCL库下载 下载链接&#xff1a; https://github.com/PointCloudLibrary/pcl/releases 下载这两个。 PCL库安装 运行.exe文件进行安装。 环境变量勾第二个&#xff08;其实无所谓&#xff0c;反正还要添加别的环境变量&#xff0c;这里没选之后加也一样&#xff09;。 安装…

金融学-货币理论

前言 前面学习了什么是货币供给&#xff0c;货币供给的决定以及联邦储备体系在货币供给中所起的作用。现在我们要开始探讨经济中货币供给在决定价格水平与全部商品和劳务(总供给)中的作用。关于货币对经济影响的研究&#xff0c;称为货币理论(monetarythe-ory) 货币数量论 古典…

Visio绘图——给多边形增加连接线

每次在画项目框图和各类爪图的时候&#xff0c;连接线是最烦人的&#xff0c;虽然选择的是折线&#xff0c;单往往事与愿违。 下面就记录一下&#xff0c;如何查找各类连接线。 1、先展开左侧菜单栏&#xff0c;点击如下所示的“&#xff1e;”2、在展开的界面&#xff0c;再次…

【开题答辩全过程】以 付费自习室系统小程序为例,包含答辩的问题和答案

个人简介一名14年经验的资深毕设内行人&#xff0c;语言擅长Java、php、微信小程序、Python、Golang、安卓Android等开发项目包括大数据、深度学习、网站、小程序、安卓、算法。平常会做一些项目定制化开发、代码讲解、答辩教学、文档编写、也懂一些降重方面的技巧。感谢大家的…

开疆智能Profinet转EtherCAT网关连接TR-Electronic传感器配置案例

本案例是通过开疆智能研发的Profinet转EtherCAT网关将传感器数据传送到PLC&#xff0c;由于两边设备采用协议不同&#xff0c;故而使用网关进行转换。网关配置&#xff1a;打开网关配置软件“EtherCAT Manager”并新建项目。根据不通网关型号也可选择ModbusTCP&#xff0c;Ethe…

VSCode中使用Markdown

文章目录1. 背景2. 安装插件3. 基础写作与预览4. 生成PDF文档5. 插入代码6. 插入图片7. 小结1. 背景 编程技术人员&#xff0c;很多人写作习惯用Markdown格式吧。 首先Markdown很简单&#xff0c;第二它的层次结构特别清晰&#xff0c;再然后它对嵌入图片、代码的支持很优秀。…

2024全栈技术栈选型指南

前后端技术栈选择现代前后端技术栈选择需兼顾市场需求与个人兴趣。前端领域React、Vue、Angular形成三足鼎立&#xff0c;React在大型项目占比达58%&#xff0c;Vue在小中型企业更受欢迎。TypeScript采用率年增长25%&#xff0c;已成为工程化标配。后端技术呈现多元化趋势&…

Spring Boot 项目文件上传安全与优化:OSS、MinIO、Nginx 分片上传实战

在实际的 Web 项目中&#xff0c;文件上传是一个常见需求&#xff1a;用户上传头像、企业后台上传资料、视频平台上传大文件等等。然而&#xff0c;文件上传也是最容易引发安全风险的功能之一&#xff0c;比如恶意脚本上传、木马文件伪装、存储空间消耗攻击。同时&#xff0c;当…

智能安防:以AI重塑安全新边界

传统安防依赖人力监控与简单报警&#xff0c;效率低下且易遗漏风险。随着人工智能、物联网及大数据技术的融合&#xff0c;智能安防正重新定义安全管理的范式&#xff0c;从被动响应转向主动预警&#xff0c;成为智慧城市与数字化生活的重要基石。智能安防的核心是人工智能视觉…