12.Java 多线程编程

✨博客主页: https://blog.csdn.net/m0_63815035?type=blog

💗《博客内容》:.NET、Java.测试开发、Python、Android、Go、Node、Android前端小程序等相关领域知识
📢博客专栏: https://blog.csdn.net/m0_63815035/category_11954877.html
📢欢迎点赞 👍 收藏 ⭐留言 📝
📢本文为学习笔记资料,如有侵权,请联系我删除,疏漏之处还请指正🙉
📢大厦之成,非一木之材也;大海之阔,非一流之归也✨

这里写目录标题

  • 前言
    • 一、多线程概述
      • [1.1 程序、进程、线程](#1.1 程序、进程、线程)
      • [1.2 并发与并行](#1.2 并发与并行)
      • [1.3 线程](#1.3 线程)
      • [1.4 多线程的优缺点](#1.4 多线程的优缺点)
    • 二、线程的创建与启动
      • [2.1 方式一:继承 Thread 类](#2.1 方式一:继承 Thread 类)
      • [2.2 方式二:实现 Runnable 接口](#2.2 方式二:实现 Runnable 接口)
      • [2.3 方式三:实现 Callable 接口 + FutureTask(可获取返回值)](#2.3 方式三:实现 Callable 接口 + FutureTask(可获取返回值))
      • [2.4 方式四:线程池(Executors)](#2.4 方式四:线程池(Executors))
    • 三、线程的基本信息与控制
      • [3.1 常用方法](#3.1 常用方法)
      • [3.2 线程优先级示例](#3.2 线程优先级示例)
      • [3.3 守护线程](#3.3 守护线程)
    • 四、线程的生命周期与状态转换
    • 五、线程安全与同步机制
      • [5.1 线程安全问题示例(卖票)](#5.1 线程安全问题示例(卖票))
      • [5.2 synchronized 同步机制](#5.2 synchronized 同步机制)
      • [5.3 synchronized 的可重入性](#5.3 synchronized 的可重入性)
      • [5.4 Lock 接口与 ReentrantLock](#5.4 Lock 接口与 ReentrantLock)
      • [5.5 synchronized vs ReentrantLock](#5.5 synchronized vs ReentrantLock)
    • 六、线程间通信(等待/通知机制)
      • [6.1 wait/notify/notifyAll 方法](#6.1 wait/notify/notifyAll 方法)
      • [6.2 生产者消费者模型(单生产者单消费者)](#6.2 生产者消费者模型(单生产者单消费者))
      • [6.3 多生产者多消费者问题](#6.3 多生产者多消费者问题)
      • [6.4 Condition(Lock 配套的条件变量)](#6.4 Condition(Lock 配套的条件变量))
    • 七、死锁与避免
      • [7.1 死锁示例](#7.1 死锁示例)
      • [7.2 死锁产生的四个必要条件](#7.2 死锁产生的四个必要条件)
      • [7.3 避免死锁的方法](#7.3 避免死锁的方法)
    • [八、volatile 关键字](#八、volatile 关键字)
      • [8.1 内存可见性问题](#8.1 内存可见性问题)
      • [8.2 volatile 的作用](#8.2 volatile 的作用)
      • [8.3 适用场景](#8.3 适用场景)
    • [九、线程池(Executor 框架)](#九、线程池(Executor 框架))
      • [9.1 为什么要使用线程池](#9.1 为什么要使用线程池)
      • [9.2 Executors 工具类提供的常见线程池](#9.2 Executors 工具类提供的常见线程池)
      • [9.3 自定义线程池](#9.3 自定义线程池)
      • [9.4 提交任务的方法](#9.4 提交任务的方法)
    • [十、ThreadLocal 详解](#十、ThreadLocal 详解)
      • [10.1 原理](#10.1 原理)
      • [10.2 典型用法](#10.2 典型用法)
      • [10.3 ThreadLocal vs synchronized](#10.3 ThreadLocal vs synchronized)
    • 十一、高级并发工具简介(`java.util.concurrent`)
      • [11.1 原子类(Atomic)](#11.1 原子类(Atomic))
      • [11.2 并发集合](#11.2 并发集合)
      • [11.3 CountDownLatch、CyclicBarrier、Semaphore](#11.3 CountDownLatch、CyclicBarrier、Semaphore)
      • [11.4 CompletableFuture(异步编程)](#11.4 CompletableFuture(异步编程))
    • 十二、最佳实践与常见误区
    • 十三、结尾

前言

多线程是 Java 语言的核心特性之一,广泛应用于服务器端开发、GUI 程序、高并发系统等场景。理解多线程不仅需要掌握创建线程的方法,更需要深刻理解线程安全、同步机制、通信方式、死锁预防以及并发工具类。本文结合源码分析和实践案例,力求全面、深入、实用。


一、多线程概述

单进程的计算机只能做一件事情,而我们现在的计算机都可以做多件事情。例如,一边玩游戏(游戏进程),一边听音乐(音乐进程)。

也就是说现在的计算机都是支持多进程的,可以在一个时间段内执行多个任务。并且,还可以提高CPU的使用率。

1.1 程序、进程、线程

概念 描述 特点
程序(Program) 静态的代码集合(如 .java.class 文件) 不占用运行资源
进程(Process) 程序的一次动态执行,是系统资源分配的单位 每个进程有独立的地址空间(代码、数据、堆栈)
线程(Thread) 进程中的一条执行路径,是 CPU 调度的基本单位 同一进程内的线程共享堆和方法区,但拥有独立的程序计数器、栈和局部变量

一个进程至少有一个线程(主线程),也可以有多个线程。多线程的意义在于充分利用 CPU 时间片,提高程序响应速度和资源利用率。

1.2 并发与并行

  • 并发(Concurrency) :在同一时间段内,多个任务交替执行(微观上串行,宏观上并行)。单核 CPU 通过快速时间片轮转实现并发。
  • 并行(Parallelism) :在同一时刻,多个任务同时执行(需要多核 CPU)。

Java 多线程既可以实现并发(单核),也可以实现并行(多核)。并行流的底层使用 ForkJoinPool

1.3 线程

线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程,也可以有多个线程。

什么是多线程呢?即就是一个程序中有多个线程在同时执行,我们也称之为多线程程序。

紧接着,我们来区别单线程程序与多线程程序的不同:

1、单线程程序:即,若有多个任务只能依次执行。当上一个任务执行结束后,下一个任务才开始执行。

2、多线程程序:即,若有多个任务可以同时执行。多个任务可以并发执行。

1.4 多线程的优缺点

优点

  • 提高 CPU 利用率
  • 简化某些模型(如生产者消费者)
  • 提升用户体验(UI 不卡顿)

缺点

  • 线程上下文切换开销(保存/恢复寄存器、程序计数器等)
  • 数据竞争导致线程安全问题
  • 死锁、活锁、饥饿等问题
  • 调试复杂度增加

二、线程的创建与启动

Java 中创建线程有 三种主要方式 ,以及 线程池(第四种)。

2.1 方式一:继承 Thread 类

java 复制代码
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " 执行");
    }
}

// 使用
MyThread t1 = new MyThread();
t1.start();   // 启动线程,JVM 调用 run()

要点

  • 重写 run() 方法,编写任务代码。
  • 调用 start() 而非直接调用 run()(直接调用 run 只是普通方法,不会创建新线程)。
  • 每个线程对象只能 start() 一次,重复调用会抛出 IllegalThreadStateException

2.2 方式二:实现 Runnable 接口

java 复制代码
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

// 使用
MyRunnable task = new MyRunnable();
Thread t = new Thread(task, "线程名");
t.start();

优点:避免单继承局限,更适合多个线程共享同一个任务对象(共享数据)。

2.3 方式三:实现 Callable 接口 + FutureTask(可获取返回值)

java 复制代码
class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= 100; i++) sum += i;
        return sum;
    }
}

// 使用
MyCallable mc = new MyCallable();
FutureTask<Integer> ft = new FutureTask<>(mc);
Thread t = new Thread(ft);
t.start();
Integer result = ft.get();  // 阻塞获取结果

特点

  • call() 方法有返回值,可以抛出受检异常。
  • 通过 FutureTask 获取结果,get() 会阻塞直到任务完成。

2.4 方式四:线程池(Executors)

后续章节详细讲解,此处简单示例:

java 复制代码
ExecutorService pool = Executors.newFixedThreadPool(3);
pool.execute(() -> System.out.println("任务"));
pool.shutdown();

三、线程的基本信息与控制

3.1 常用方法

方法 说明
Thread.currentThread() 获取当前正在执行的线程对象
getName() / setName(String) 获取/设置线程名称
getPriority() / setPriority(int) 优先级 1~10,默认为 5。优先级高只是获得 CPU 的概率高,不保证顺序
isAlive() 线程是否处于活动状态(已启动且未死亡)
Thread.sleep(long millis) 让当前线程休眠(进入阻塞状态),不释放锁
yield() 让出 CPU,从运行状态变为就绪状态,可能立即又被调度
join() 等待该线程终止(当前线程阻塞直到目标线程结束)
setDaemon(boolean) 设置为守护线程(必须在 start 之前),当所有用户线程结束时 JVM 退出
interrupt() 中断线程(设置中断标志)

3.2 线程优先级示例

java 复制代码
Thread high = new Thread(() -> {
    for (int i = 0; i < 5; i++) System.out.println("high");
});
high.setPriority(Thread.MAX_PRIORITY);
Thread low = new Thread(() -> {
    for (int i = 0; i < 5; i++) System.out.println("low");
});
low.setPriority(Thread.MIN_PRIORITY);
high.start();
low.start();

优先级并不能保证执行顺序,只是调度器的建议。

3.3 守护线程

java 复制代码
Thread daemon = new Thread(() -> {
    while (true) System.out.println("守护线程运行");
});
daemon.setDaemon(true);
daemon.start();
// 主线程结束,守护线程自动终止

守护线程常用于后台监控、垃圾回收等。注意:守护线程中不应该执行 I/O 等必须完成的操作,因为 JVM 退出时不会等待守护线程。


四、线程的生命周期与状态转换

Java 线程有六种状态(Thread.State 枚举):

状态 说明
NEW 线程对象已创建,但未调用 start()
RUNNABLE 就绪(可运行)或正在运行,等待 CPU 时间片
BLOCKED 等待获取监视器锁(synchronized 块/方法)
WAITING 无限期等待,需要其他线程显式唤醒(wait()join() 无参)
TIMED_WAITING 有限期等待,超时后自动唤醒(sleep(long)wait(long)join(long)
TERMINATED 线程执行结束(正常退出或异常)

状态转换图(文字描述):

  • NEW → RUNNABLE:调用 start()
  • RUNNABLE ↔ BLOCKED:竞争 synchronized 锁失败
  • RUNNABLE → WAITING:调用 wait()join()(无超时)、LockSupport.park()
  • WAITING → RUNNABLE:notify()/notifyAll()join 线程结束
  • RUNNABLE → TIMED_WAITING:调用 sleep()、带超时的 wait()/join()
  • TIMED_WAITING → RUNNABLE:超时或唤醒
  • RUNNABLE → TERMINATED:run() 正常结束或异常退出

注意Thread.stop() 已废弃,因为它会立即释放所有锁,可能导致数据不一致。


五、线程安全与同步机制

5.1 线程安全问题示例(卖票)

java 复制代码
class TicketRunnable implements Runnable {
    private int tickets = 100;
    @Override
    public void run() {
        while (true) {
            if (tickets > 0) {
                try { Thread.sleep(1); } catch (InterruptedException e) {}
                System.out.println(Thread.currentThread().getName() + " 卖出第 " + tickets-- + " 张票");
            } else break;
        }
    }
}

运行后可能出现:

  • 同一张票被多个线程卖出(负数票)
  • 票数错乱

原因tickets > 0 判断后,线程被切换,另一个线程也进入判断,导致重复卖出。

5.2 synchronized 同步机制

synchronized 可以修饰代码块方法,保证同一时刻只有一个线程执行该代码块。

同步代码块
java 复制代码
synchronized (lockObject) {
    // 临界区代码
}

锁对象可以是任意对象,多个线程必须使用同一个锁对象才能互斥。

同步方法
  • 非静态同步方法:锁是 this(当前实例)
  • 静态同步方法:锁是 Class 对象(类名.class
java 复制代码
public synchronized void method() { }   // 锁 this
public static synchronized void staticMethod() { } // 锁 Class

卖票问题解决方案

java 复制代码
class TicketRunnable implements Runnable {
    private int tickets = 100;
    private final Object lock = new Object();
    @Override
    public void run() {
        while (true) {
            synchronized (lock) {
                if (tickets <= 0) break;
                System.out.println(Thread.currentThread().getName() + " 卖出第 " + tickets-- + " 张票");
            }
        }
    }
}

5.3 synchronized 的可重入性

同一个线程已经持有锁,再次请求该锁时会成功(嵌套调用)。例如:

java 复制代码
public synchronized void a() { b(); }
public synchronized void b() { }

线程调用 a() 时获得锁,调用 b() 时发现锁已被自己持有,直接进入,不会死锁。

5.4 Lock 接口与 ReentrantLock

synchronized 是隐式锁,加锁释放锁自动完成。Lock 是显式锁,需要手动加锁解锁。

java 复制代码
Lock lock = new ReentrantLock();
lock.lock();
try {
    // 临界区
} finally {
    lock.unlock();   // 必须确保释放
}

ReentrantLock 特性

  • 可重入(与 synchronized 相同)
  • 可中断锁:lockInterruptibly()
  • 可超时:tryLock(long time, TimeUnit unit)
  • 公平锁:构造参数 true 表示按照等待时间顺序获取锁(默认非公平)
  • 可绑定多个 Condition 对象

示例:使用 ReentrantLock 实现卖票

java 复制代码
class TicketRunnable implements Runnable {
    private int tickets = 100;
    private final Lock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true) {
            lock.lock();
            try {
                if (tickets <= 0) break;
                System.out.println(Thread.currentThread().getName() + " 卖出第 " + tickets-- + " 张票");
            } finally {
                lock.unlock();
            }
        }
    }
}

5.5 synchronized vs ReentrantLock

特性 synchronized ReentrantLock
获取锁方式 隐式 显式 lock/unlock
可中断 不支持(除非使用 wait 机制) 支持 lockInterruptibly()
超时尝试 不支持 支持 tryLock()
公平锁 非公平 可指定公平/非公平
条件变量 wait/notify(一个等待集) 多个 Condition
性能 优化后接近 高并发下略优
语法简洁性 简洁 需注意 finally 释放

选择建议 :一般情况下优先使用 synchronized,代码更简单;需要高级特性(如可中断、超时、多条件)时使用 ReentrantLock


六、线程间通信(等待/通知机制)

6.1 wait/notify/notifyAll 方法

这些方法定义在 Object 类中,必须在 synchronized 代码块或方法中调用,且必须通过锁对象调用。

  • wait():使当前线程进入 WAITING 状态,并释放锁
  • notify():唤醒在同一个锁上等待的一个线程(具体哪个由 JVM 决定)。
  • notifyAll():唤醒所有等待线程。

错误用法 :调用 wait()notify() 时没有持有锁会抛出 IllegalMonitorStateException

6.2 生产者消费者模型(单生产者单消费者)

使用 wait/notify 实现简单的交替生产消费。

java 复制代码
class Resource {
    private String data;
    private boolean hasData = false;

    public synchronized void produce(String value) throws InterruptedException {
        while (hasData) {        // 用 while 防止虚假唤醒
            wait();
        }
        data = value;
        hasData = true;
        System.out.println("生产:" + data);
        notify();                 // 通知消费者
    }

    public synchronized void consume() throws InterruptedException {
        while (!hasData) {
            wait();
        }
        System.out.println("消费:" + data);
        hasData = false;
        notify();
    }
}

6.3 多生产者多消费者问题

使用 while 替代 if(防止虚假唤醒)和 notifyAll(避免某些线程永远等不到唤醒)。但 notifyAll 会唤醒所有等待线程(包括同类线程),导致效率下降。

更好的解决方案 :使用 Lock + Condition 实现定向唤醒。

6.4 Condition(Lock 配套的条件变量)

java 复制代码
class Resource {
    private String data;
    private boolean hasData = false;
    private final Lock lock = new ReentrantLock();
    private final Condition producerCond = lock.newCondition();
    private final Condition consumerCond = lock.newCondition();

    public void produce(String value) throws InterruptedException {
        lock.lock();
        try {
            while (hasData) {
                producerCond.await();
            }
            data = value;
            hasData = true;
            System.out.println("生产:" + data);
            consumerCond.signal();   // 只唤醒消费者
        } finally {
            lock.unlock();
        }
    }

    public void consume() throws InterruptedException {
        lock.lock();
        try {
            while (!hasData) {
                consumerCond.await();
            }
            System.out.println("消费:" + data);
            hasData = false;
            producerCond.signal();   // 只唤醒生产者
        } finally {
            lock.unlock();
        }
    }
}

Conditionawait()signal()signalAll() 替代 waitnotifynotifyAll,且可以区分不同的等待队列。


七、死锁与避免

7.1 死锁示例

java 复制代码
Object lockA = new Object();
Object lockB = new Object();

Thread t1 = new Thread(() -> {
    synchronized (lockA) {
        sleep(100);
        synchronized (lockB) { /* ... */ }
    }
});
Thread t2 = new Thread(() -> {
    synchronized (lockB) {
        sleep(100);
        synchronized (lockA) { /* ... */ }
    }
});

两个线程相互等待对方释放锁,导致无限阻塞。

7.2 死锁产生的四个必要条件

  1. 互斥:资源一次只能被一个线程使用。
  2. 请求与保持:线程持有至少一个资源,并等待其他资源。
  3. 不可剥夺:资源只能由持有者主动释放。
  4. 循环等待:存在等待环。

7.3 避免死锁的方法

  • 破坏循环等待:按固定顺序获取锁(如总是先锁 A 后锁 B)。
  • 使用 tryLock 超时放弃。
  • 使用 Lock 的可中断锁机制。
  • 减少同步代码块嵌套。

八、volatile 关键字

8.1 内存可见性问题

在多核 CPU 中,每个线程可能将共享变量缓存到 CPU 缓存中,导致一个线程修改了变量但另一个线程不可见。

8.2 volatile 的作用

  • 保证可见性:对一个 volatile 变量的写操作会立即刷新到主内存,读操作从主内存读取。
  • 禁止指令重排序:编译器和处理器不会对 volatile 变量相关的操作进行重排序。

volatile 不保证原子性 。例如 volatile int count; count++ 仍然是线程不安全的(因为读-改-写不是原子操作)。

8.3 适用场景

  • 布尔标志位(如 volatile boolean running = true;
  • 一次性安全发布(如双重检查锁中的 volatile 禁止重排序)
java 复制代码
public class Singleton {
    private static volatile Singleton instance;
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) instance = new Singleton();
            }
        }
        return instance;
    }
}

九、线程池(Executor 框架)

9.1 为什么要使用线程池

  • 避免频繁创建/销毁线程的开销。
  • 限制并发线程数量,防止资源耗尽。
  • 统一管理线程的生命周期。

9.2 Executors 工具类提供的常见线程池

方法 说明 适用场景
newFixedThreadPool(int n) 固定大小线程池,无界队列 任务数量可控,限制最大并发
newSingleThreadExecutor() 单线程池,顺序执行 保证任务串行
newCachedThreadPool() 可缓存线程池,空闲 60 秒回收 大量短任务,弹性伸缩
newScheduledThreadPool(int core) 支持定时/延迟任务 调度任务

示例

java 复制代码
ExecutorService fixed = Executors.newFixedThreadPool(5);
for (int i = 0; i < 20; i++) {
    fixed.execute(() -> System.out.println(Thread.currentThread().getName()));
}
fixed.shutdown();

9.3 自定义线程池

ThreadPoolExecutor 提供更细致的参数:

java 复制代码
ThreadPoolExecutor pool = new ThreadPoolExecutor(
    corePoolSize,      // 核心线程数
    maximumPoolSize,   // 最大线程数
    keepAliveTime,     // 空闲存活时间
    TimeUnit.SECONDS,
    new LinkedBlockingQueue<>(),   // 工作队列
    Executors.defaultThreadFactory(),
    new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);

拒绝策略

  • AbortPolicy:抛出异常(默认)
  • CallerRunsPolicy:由提交任务的线程执行
  • DiscardPolicy:丢弃任务
  • DiscardOldestPolicy:丢弃队列头部任务,重试提交

9.4 提交任务的方法

  • execute(Runnable):无返回值
  • submit(Callable):返回 Future<T>
  • submit(Runnable):返回 Future<?>
  • invokeAll(Collection<Callable>):批量执行并等待所有完成
  • invokeAny(...):执行集合,返回第一个完成的结果

十、ThreadLocal 详解

10.1 原理

ThreadLocal 为每个线程提供独立的变量副本,每个线程修改的是自己的副本,互不干扰。底层是每个 Thread 对象持有一个 ThreadLocalMapThreadLocal 作为键,副本值作为值。

10.2 典型用法

java 复制代码
public class ThreadLocalExample {
    private static final ThreadLocal<SimpleDateFormat> dateFormat = 
        ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
    
    public static void main(String[] args) {
        Runnable task = () -> {
            System.out.println(dateFormat.get().format(new Date()));
            // 不需要显式 remove,但建议在使用结束后调用 remove() 避免内存泄漏
        };
        new Thread(task).start();
    }
}

注意:使用线程池时,线程复用可能导致 ThreadLocal 值残留,必须在使用后调用 remove()

10.3 ThreadLocal vs synchronized

  • synchronized:以时间换空间,多个线程排队访问共享数据。
  • ThreadLocal:以空间换时间,每个线程拥有自己的副本,无需同步。

十一、高级并发工具简介(java.util.concurrent

11.1 原子类(Atomic)

AtomicIntegerAtomicReference,使用 CAS 实现无锁线程安全。

java 复制代码
AtomicInteger count = new AtomicInteger(0);
count.incrementAndGet();  // 原子加1

11.2 并发集合

  • ConcurrentHashMap:分段锁(JDK7)/ CAS+synchronized(JDK8)
  • CopyOnWriteArrayList:读多写少场景
  • BlockingQueue:阻塞队列(ArrayBlockingQueue, LinkedBlockingQueue

11.3 CountDownLatch、CyclicBarrier、Semaphore

  • CountDownLatch:一个线程等待多个线程完成。
  • CyclicBarrier:多个线程互相等待,达到屏障后一起执行。
  • Semaphore:控制同时访问资源的线程数量(限流)。

11.4 CompletableFuture(异步编程)

Java 8 引入,支持链式异步回调。

java 复制代码
CompletableFuture.supplyAsync(() -> "Hello")
    .thenApply(s -> s + " World")
    .thenAccept(System.out::println);

十二、最佳实践与常见误区

  1. 不要使用 Thread.stop()suspend()resume(),已废弃且不安全。
  2. 使用 volatile 或原子类处理简单的状态标志,避免过度同步。
  3. 优先使用 synchronized 而非 Lock,除非需要额外功能。
  4. 注意死锁 :尽量按相同顺序获取锁,使用 tryLock 超时。
  5. 线程池使用完毕要 shutdown(),否则程序可能不会退出。
  6. 不要在 run() 方法中抛出受检异常 ,只能使用 try-catch
  7. 避免在持有锁时调用外部方法(可能引起死锁或性能问题)。
  8. 使用 ThreadLocal 后务必 remove()(尤其在线程池中)。

十三、结尾

多线程是 Java 进阶的必经之路,理解线程的生命周期、同步机制和通信方式是核心。掌握 synchronizedLockvolatile 的区别与使用场景,熟悉 wait/notifyCondition,能够设计出正确且高效的多线程程序。同时,线程池和并发工具类大大简化了并发编程的复杂度,是实际项目中的首选。

最后,多线程问题的调试往往比较困难,建议多用 jstackjconsolevisualvm 等工具分析线程状态,逐步积累经验。

csharp 复制代码
今天这篇文章就到这里了,大厦之成,非一木之材也;大海之阔,非一流之归也。感谢大家观看本文
相关推荐
xuhaoyu_cpp_java1 小时前
项目学习(三)代码生成器
java·经验分享·笔记·学习
乐观勇敢坚强的老彭1 小时前
C++信息学奥赛lesson1
java·开发语言·c++
San813_LDD1 小时前
[深度学习] 数据序列化格式对比:以日志级别配置为例
xml·java·前端
jllllyuz1 小时前
MATLAB实现滚动轴承故障诊断(外圈故障)
开发语言·人工智能·matlab
github_czy1 小时前
更加优雅的类型检查与传参---mcp源码分析
java·服务器·开发语言
专注_每天进步一点点1 小时前
IDEA中,Apifox Helper 的 2.0.15-243版本的插件 导出指定的接口,入参的中文名为空,描述为空
java·ide·intellij-idea
Irissgwe1 小时前
C++ STL关联式容器详解:set、multiset、map、multimap
开发语言·c++·stl·set·map·multiset·关联式容器
兰令水1 小时前
leecodecode【区间DP+树形DP】【2026.6.10打卡-java版本】
java·算法·leetcode
叶帆2 小时前
【YFIOs】用C#开发硬件之WiFi网络
开发语言·网络·c#