当前位置: 首页 > news >正文

java多线程及线程池

线程

  • 一、什么是多线程?
  • 二、线程的生命周期
  • 三、简单地创建一个线程
    • 1、实现Runnable接口
    • 2、继承Thread类
    • 3、使用Callable和FutureTask
    • 4、三种实现方式的对比
  • 四、线程同步和锁
    • 1、为什么需要线程同步?
    • 2、线程同步的实现方式
    • 3、synchronized和ReentrantLock使用示例
  • 五、线程池
    • 1、Executor接口
    • 2、ExecutorService接口
      • isShutdown() 和 isTerminated()的区别
    • 3、Executors工厂类
    • 4、Executors示例
    • 5、一般使用ThreadPoolExecutor而不是Executors
    • 6、ThreadPoolExecutor示例
    • 7、线程池的关闭
      • shutdownNow()和shutdown()的区别
      • 注意处理InterruptedException
      • 什么是阻塞操作?

一、什么是多线程?

多线程是指在一个程序中同时运行多个线程,每个线程都是独立的执行流程,可以在同一时间内执行不同的任务,从而提高程序的并发性和效率。

二、线程的生命周期

在Thread.State枚举类中可以看到线程的如下阶段(状态)

阶段说明补充
新建(New)从新建一个线程对象到程序start() 这个线程之间的状态,都是新建状态。在这个状态下,JVM已经为此线程分配了必要的内存。
就绪(Runnable)线程对象调用start()方法后,就处于就绪状态。就绪状态的线程在获得CPU时间片后就可以开始运行。这个状态的线程位于可运行线程池中,等待被线程调度选中,获得CPU的使用权。
运行状态(Running)就绪状态下的线程在获取CPU资源后就可以执行run(),此时的线程便处于运行状态。运行状态的线程可变为就绪、阻塞及死亡三种状态。
阻塞状态(Blocked)当一个线程试图获取一个内部的对象锁(也就是进入一个synchronized块),而该锁被其他线程持有,则该线程进入阻塞状态。阻塞状态的线程在锁被释放时,将会进入就绪状态。
等待状态(Waiting)线程通过调用其自身的wait()方法、join()方法或LockSupport.park()方法,或者通过调用其他线程的join()方法,可以进入等待状态。在等待状态的线程不会被分配CPU时间片,它们只能通过被其他线程显式唤醒进入就绪状态。
超时等待状态(Timed Waiting)当线程调用了sleep(long ms),wait(long ms),join(long ms),或者LockSupport.parkNanos(), LockSupport.parkUntil()等具有指定等待时间的方法,线程就会进入超时等待状态。当超时时间到达后,线程会自动返回到就绪状态。
终止状态(Terminated)当线程的run()方法执行完毕,或者线程中断,线程就会进入终止状态在这个状态下,线程已经完成了它的全部工作。
Thread t = new Thread(); // 线程此时处于New状态
t.start(); // 线程此时处于Runnable状态
t.wait();  // 线程此时处于Waiting状态
t.join();  // 线程此时处于Waiting状态
Thread.sleep(1000); // 线程此时处于Timed Waiting状态

三、简单地创建一个线程

1、实现Runnable接口

最简洁的方式是直接用lambda表达式:

Thread thread = new Thread(() -> {while (true) {//具体逻辑}
});
thread.setName("Thread-Example");
thread.start();

调用start 与 run方法的区别:
直接调用 run() 是在主线程中执行了 run(),没有启动新的线程。
使用 start() 是启动新的线程,通过新的线程间接执行 run()方法中的代码。

上面的lambda表达式相当于使用了匿名内部类:

Thread thread = new Thread(new Runnable() {@Overridepublic void run() {while (true) {// 具体逻辑}}
});

实现Runnable接口完整的写法是这样的:

// 定义一个Runnable接口的实现类,任何你想用于多线程的类,来实现Runnable接口
class MyRunnable implements Runnable {@Overridepublic void run() {// 定义线程需要执行的逻辑System.out.println("这是一个实现Runnable接口的线程");}
}public class Main {public static void main(String[] args) {// 创建Runnable对象MyRunnable runnable = new MyRunnable();// 创建线程对象,并将Runnable对象传递给Thread构造方法Thread thread = new Thread(runnable);// 启动线程thread.start();}
}

2、继承Thread类

// 定义一个Thread的子类
class MyThread extends Thread {@Overridepublic void run() {// 定义线程需要执行的逻辑System.out.println("这是一个继承Thread类的线程");}
}public class Main {public static void main(String[] args) {// 创建线程对象MyThread thread = new MyThread();// 启动线程thread.start();}
}

3、使用Callable和FutureTask

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;class MyCallable implements Callable<String> {@Overridepublic String call() throws Exception {// 线程需要执行的逻辑System.out.println("这是一个实现Callable接口的线程");return "子线程执行完成,返回结果。";}
}public class Main {public static void main(String[] args) {MyCallable callable = new MyCallable();FutureTask<String> futureTask = new FutureTask<>(callable);Thread thread = new Thread(futureTask);thread.start();try {String result = futureTask.get();System.out.println("主线程获取到结果:" + result);} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}}
}

4、三种实现方式的对比

特性继承Thread类实现Runnable接口使用Callable和FutureTask
继承/实现继承Thread类实现Runnable接口实现Callable接口
单继承限制受单继承限制,无法继承其他类可以继承其他类可以继承其他类
代码复用性代码复用性较差代码复用性更好代码复用性更好
适用场景简单的线程逻辑,不需要继承其他类复杂逻辑,需要继承其他类的场景需要返回结果的异步任务,结合线程池使用
资源共享不支持资源共享支持资源共享支持资源共享
结果返回不支持不支持支持
异常处理run()方法不声明异常,需在方法内处理run()方法不声明异常,需在方法内处理call()方法可声明异常,主线程可捕获

四、线程同步和锁

1、为什么需要线程同步?

在多线程环境中,多个线程可能会同时访问和修改同一个共享资源,这可能导致数据不一致和其他并发问题。例如,在银行账户的存取款操作中,如果不进行适当的同步,可能会出现以下情况:

数据不一致:两个线程同时读取账户余额,各自进行操作后,写回不同的结果,导致账户余额错误。
竞态条件:多个线程在竞争共享资源,导致程序执行结果不可预测。
为了避免这些问题,必须使用线程同步机制,确保在同一时间只有一个线程能够访问和修改共享资源。

2、线程同步的实现方式

Java提供了多种线程同步机制,包括:

synchronized关键字:可以修饰方法或代码块,确保在同一时间只有一个线程执行同步代码。
Lock接口:提供了更灵活的锁定机制,可以实现更复杂的同步逻辑。
atomic变量:使用Atomic类来实现线程安全的变量操作,避免加锁。
volatile关键字:确保变量的内存可见性,避免由于缓存不一致导致的问题。

3、synchronized和ReentrantLock使用示例

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;// 银行账户类
class BankAccount {private double balance;// 使用synchronized关键字修饰的存款方法public synchronized void deposit(double amount) {balance += amount;System.out.println("存款后余额:" + balance);}// 使用synchronized关键字修饰的取款方法public synchronized void withdraw(double amount) {if (balance >= amount) {balance -= amount;System.out.println("取款后余额:" + balance);} else {System.out.println("余额不足!");}}// 使用Lock实现的存款方法private final Lock lock = new ReentrantLock();public void depositWithLock(double amount) {lock.lock();try {balance += amount;System.out.println("Lock存款后余额:" + balance);} finally {lock.unlock();}}// 使用Lock实现的取款方法public void withdrawWithLock(double amount) {lock.lock();try {if (balance >= amount) {balance -= amount;System.out.println("Lock取款后余额:" + balance);} else {System.out.println("Lock余额不足!");}} finally {lock.unlock();}}
}// 模拟银行账户操作的线程
class TransactionThread implements Runnable {private BankAccount account;private double amount;private boolean isUsingSynchronized;public TransactionThread(BankAccount account, double amount, boolean isUsingSynchronized) {this.account = account;this.amount = amount;this.isUsingSynchronized = isUsingSynchronized;}@Overridepublic void run() {try {for (int i = 0; i < 5; i++) {if (isUsingSynchronized) {account.deposit(amount);account.withdraw(amount);} else {account.depositWithLock(amount);account.withdrawWithLock(amount);}Thread.sleep(100);}} catch (InterruptedException e) {Thread.currentThread().interrupt();}}
}public class ThreadSynchronizationExample {public static void main(String[] args) {// 创建银行账户实例BankAccount account = new BankAccount();// 使用synchronized关键字的线程System.out.println("=== 使用synchronized关键字 ===");Thread thread1 = new Thread(new TransactionThread(account, 100, true));Thread thread2 = new Thread(new TransactionThread(account, 50, true));thread1.start();thread2.start();// 等待synchronized线程完成try {thread1.join();thread2.join();} catch (InterruptedException e) {e.printStackTrace();}// 使用Lock接口的线程System.out.println("\n=== 使用Lock接口 ===");Thread thread3 = new Thread(new TransactionThread(account, 200, false));Thread thread4 = new Thread(new TransactionThread(account, 150, false));thread3.start();thread4.start();// 等待Lock线程完成try {thread3.join();thread4.join();} catch (InterruptedException e) {e.printStackTrace();}}
}

五、线程池

Java的java.util.concurrent(简称JUC)包中提供了一套丰富的线程池工具。
该部分内容参考原文链接

1、Executor接口

Executor接口是JUC包中定义的一个执行器接口,它只有一个execute方法,接收一个Runnable对象作为参数,并执行Runnable中的操作。这个接口非常简单,但它定义了执行器的基本功能。

public interface Executor {void execute(Runnable command);
}

在实际应用中,我们通常不会直接使用Executor接口,而是使用它的子接口ExecutorService,它提供了更丰富的功能。

2、ExecutorService接口

ExecutorService接口继承自Executor接口,并增加了关于执行器服务的定义。它提供了一系列的方法,包括关闭执行器、立即关闭、检查执行器是否关闭、等待任务终止、提交有返回值的任务以及批量提交任务等。

public interface ExecutorService extends Executor {void shutdown(); //关闭执行器,已提交的任务会继续执行,但不接受新的任务。List<Runnable> shutdownNow(); //立即关闭执行器,尝试停止所有正在执行的任务,并返回等待执行的任务列表。boolean isShutdown(); //检查执行器是否已关闭。boolean isTerminated();  //检查执行器是否已终止,即所有任务都已完成。boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException; //等待任务终止,如果超过指定时间则返回false。<T> Future<T> submit(Callable<T> task); //提交一个有返回值的任务,并返回一个Future对象,通过该对象可以查看任务执行是否完成,并获取返回值。<T> Future<T> submit(Runnable task, T result); //提交一个Runnable任务和一个结果值,当任务执行完成后,返回该结果值。注意这个结果值是在任务执行前就确定的,与任务的实际执行结果无关。如果希望获取任务的实际执行结果,应该使用Callable任务。Future<?> submit(Runnable task); //提交一个Runnable任务,并返回一个Future对象。由于Runnable任务没有返回值,所以这个Future对象的get方法将返回null。这个方法主要用于将Runnable任务转换为Future对象,以便使用Future的相关功能(如取消任务、检查任务是否完成等)。但这个用法并不常见,因为Runnable任务本身就不支持返回值。更常见的做法是直接使用execute(Runnable command)方法执行Runnable任务。                        <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException; //批量提交Callable任务,并返回一个Future对象的列表。当所有任务都完成后,可以通过这些Future对象获取任务的返回值。如果某个任务执行失败,那么对应的Future对象的get方法将抛出ExecutionException异常。这个方法会等待所有任务都完成后才返回。如果希望设置超时时间,可以使用另一个重载版本的方法。<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException;<T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException; //批量提交Callable任务,并返回第一个成功完成的任务的返回值。当找到第一个成功完成的任务后,该方法会立即返回,而不会等待其他任务完成。如果所有任务都失败,那么该方法将抛出ExecutionException异常。这个方法通常用于实现“多个路径中选择一个最快路径”的场景。同样地,这个方法也有一个设置超时时间的重载版本。<T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}

isShutdown() 和 isTerminated()的区别

isShutdown(): 判断线程池是否调用了 shutdown() 或 shutdownNow()。
isTerminated(): 判断线程池是否已完全终止(即所有任务都已完成)。

3、Executors工厂类

Executors是一个工厂类,它提供了一系列静态方法来创建不同类型的线程池。这些线程池都是ExecutorService接口的实现类。通过Executors的工厂方法,我们可以非常方便地创建和管理线程池。

需要注意的是,尽量不要用该类创建线程池。
因为Executors.newCachedThreadPool()根据需求创建线程,最大线程数为Integer.MAX_VALUE,可能导致过多线程消耗资源。
而Executors.newFixedThreadPool(int)和Executors.newSingleThreadExecutor()虽然限制了线程池的数量,但队列最大容量为Integer.MAX_VALUE,也可能导致内存溢出。
因此我们一般使用ThreadPoolExecutor

//创建一个大小为10的固定线程池。
//创建时指定线程池的大小,当有新任务提交时,如果线程池中有空闲线程,则使用空闲线程执行任务;如果没有空闲线程,则新任务会等待直到有线程空闲出来。这种线程池适用于已知并发压力的情况下,对线程数做限制,避免由于大量线程的创建和销毁带来的性能开销。
ExecutorService executor = Executors.newFixedThreadPool(10);  
//创建一个单线程执行器。
//所有提交的任务都会按照提交的顺序依次执行,这种线程池适用于需要保证任务执行顺序的场景。
ExecutorService executor = Executors.newSingleThreadExecutor(); 
//创建一个可缓存的线程池。
//可缓存的线程池,这种线程池会根据需要创建线程来执行任务,并且可以重复利用已存在的线程来执行新的任务。当线程池中的线程在一定时间内没有执行任务时,它会被自动销毁以释放资源。
//这种线程池适用于并发压力较大且任务执行时间较短的场景,如Web服务器处理HTTP请求等。但需要注意的是,在实际应用中我们可能需要更加谨慎地使用CachedThreadPool,因为如果不当使用可能会导致系统资源耗尽(如创建过- 多的线程导致内存溢出等)。因此在使用CachedThreadPool时需要特别关注任务的执行时间和数量以及系统的资源状况等因素。
ExecutorService executor = Executors.newCachedThreadPool(); 

4、Executors示例

@Component
public class ThreadMgr {private volatile ExecutorService testThreadPoolExecutor;public ExecutorService getTestExecutor() {if (testThreadPoolExecutor == null) {synchronized (this) {if (testThreadPoolExecutor == null) {testThreadPoolExecutor = Executors.newFixedThreadPool(3);}}}return testThreadPoolExecutor;}
}@Component
public class TaskInit implements ApplicationRunner {@Autowiredprivate ThreadMgr threadMgr;@Overridepublic void run(ApplicationArguments args) {//省略数据库查询等逻辑//获取线程池并执行相关逻辑threadMgr.getTestExecutor().submit(() -> XXX);}
}

注意这里的TaskInit去继承ApplicationRunner并把逻辑放在run里面,只是为了程序启动时执行run方法中的内容。而threadMgr.getTaskExecutor().submit(() -> XXX);这一句中lambda表达式后面的部分(即XXX,可以是调用的方法等),实际上还是实现了Runnable的run方法,相当于

threadMgr.getTaskExecutor().submit(new Runnable() {@Overridepublic void run() {// 具体逻辑XXX}
});

5、一般使用ThreadPoolExecutor而不是Executors

ThreadPoolExecutor继承自AbstractExecutorService,而AbstractExecutorService实现了ExecutorService接口。
ThreadPoolExecutor也是Executors的底层实现。

该部分内容参考博客原文

ThreadPoolExecutor构造函数:

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,Executors.defaultThreadFactory(), defaultHandler);
}public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,threadFactory, defaultHandler);
}public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,Executors.defaultThreadFactory(), handler);
}public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler){//省略具体内容
}
参数含义是否必填
corePoolSize线程池中核心线程数的最大值
maximumPoolSize线程池中能拥有最多线程数
keepAliveTime表示空闲线程的存活时间
TimeUnitkeepAliveTime的单位
workQueue用于缓存任务的阻塞队列
threadFactory指定创建线程的工厂,不指定时默认使用Executors.defaultThreadFactory()
handler表示当 workQueue 已满,且池中的线程数达到 maximumPoolSize 时,线程池拒绝添加新任务时采取的策略;不指定时默认策略为AbortPolicy(),即抛出异常,由开发人员进行处理。

6、ThreadPoolExecutor示例

@Component
public class ThreadMgr {private volatile ExecutorService testThreadPoolExecutor2;public ExecutorService getTestExecutor2() {if (testThreadPoolExecutor2 == null) {synchronized (this) {if (testThreadPoolExecutor2 == null) {//这里只是给了简单的示例,实际可以通过读取配置等方式限制testThreadPoolExecutor2 = new ThreadPoolExecutor(10,20,6000,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<>(20000));}}}return testThreadPoolExecutor2;}
}//其他类中调用
threadMgr.getTestExecutor2().submit(() -> XXX);

注意这里的synchronized 主要用于确保仅在一个线程中初始化 testThreadPoolExecutor2,确保线程池的单例性。
使用线程池中的任务时,仍需确保任务自身的线程安全,特别是涉及共享状态时。

7、线程池的关闭

前文我们已经明白,可以通过ExecutorService类的shutdownNow()和shutdown()去关闭线程池。

shutdownNow()和shutdown()的区别

调用shutdown(); 已提交的任务会继续执行,但不接受新的任务。
但调用shutdownNow(); 则会去尝试停止所有正在执行的任务。但是注意,并不是所有任务都一定会成功被关闭。

shutdownNow() 通过调用 Thread.interrupt() 向所有由 executor 管理的线程发送中断信号。
这些线程的中断标志会被设置为 true,并且如果它们在执行阻塞操作,会抛出InterruptedException。但如果是非阻塞操作,则不会被打断,因此我们在代码中也可以加入if(Thread.currentThread().isInterrupted())的判断,来检查线程是否中断,以及时进行需要的处理。

注意处理InterruptedException

在某些情况下,即使线程被中断,JVM也可能不会抛出 InterruptedException。因此在有阻塞操作的代码块中手动catch InterruptedException,catch后调用Thread.currentThread().interrupt(); 可以确保其他依赖于中断的方法可以正常工作。

什么是阻塞操作?

1、等待与睡眠

Thread.sleep(long millis):使当前线程进入睡眠状态,直到指定时间过去或被中断。
Object.wait():使当前线程等待,直到其他线程调用 notify() 或 notifyAll()。
Thread.join():等待指定线程完成执行。

2、I/O 操作

文件输入输出:如 FileInputStream.read(), FileOutputStream.write()
网络通信:如 Socket.receive(), Socket.send()
数据库操作(查询、事务提交或回滚):如 ResultSet.next(), Statement.executeQuery()

3、 锁与同步

使用 synchronized 关键字的同步块:如果其他线程持有锁,当前线程会阻塞,直到锁被释放。
使用 Lock 接口:如 lock.lock()(如果锁已被占用,线程会阻塞,直到获取锁)。
使用 Semaphore:如果信号量不可用,线程会阻塞,直到信号量被释放。

4、线程通信

使用 Object.wait() 和 Object.notify() 进行线程间通信。
使用 Condition 接口:如 await(),会阻塞线程,直到被信号唤醒。

5、 远程调用

REST API 调用:如 HttpURLConnection 或 OkHttp 发送请求,等待服务器响应。
RPC(远程过程调用):如 gRPC、Dubbo 等,等待远程服务返回结果。

6、其他阻塞操作

使用 BlockingQueue 的 put() 和 take() 方法:如果队列已满(put())或为空(take()),线程会阻塞。
使用 CountDownLatch.await():等待计数器归零。
使用 CyclicBarrier.await():等待所有线程到达屏障。

相关文章:

java多线程及线程池

线程 一、什么是多线程&#xff1f;二、线程的生命周期三、简单地创建一个线程1、实现Runnable接口2、继承Thread类3、使用Callable和FutureTask4、三种实现方式的对比 四、线程同步和锁1、为什么需要线程同步&#xff1f;2、线程同步的实现方式3、synchronized和ReentrantLock…...

Unreal5从入门到精通之如何在 C++ 中创建 UserWidget

文章目录 前言UUserWidget 子类示例创建我们的 C++ 类的新蓝图子类更改现有蓝图的父类现在我们有了 C++ 基类,下一步做什么?蓝图还是 C++?结论前言 在之前的教程中,我展示了如何在编辑器中创建 UserWidget 蓝图, 在本教程中,我们将创建一个新的基于 C++ 的子类UUserWid…...

Elasticsearch:探索 CLIP 替代方案

作者&#xff1a;来自 Elastic Jeffrey Rengifo 及 Toms Mura 分析图像到图像和文本到图像搜索的 CLIP 模型的替代方案。 在本文中&#xff0c;我们将通过一个模拟房地产网站的实际示例介绍 CLIP 多模态模型&#xff0c;探索替代方案&#xff0c;并分析它们的优缺点&#xff0c…...

天翼云910B部署DeepSeek蒸馏70B LLaMA模型实践总结

一、项目背景与目标 本文记录在天翼云昇腾910B服务器上部署DeepSeek 70B模型的全过程。该模型是基于LLaMA架构的知识蒸馏版本&#xff0c;模型大小约132GB。 1.1 硬件环境 - 服务器配置&#xff1a;天翼云910B服务器 - NPU&#xff1a;8昇腾910B (每卡64GB显存) - 系统内存&…...

Mac 清理缓存,提高内存空间

步骤 1.打开【访达】 2.菜单栏第五个功能【前往】&#xff0c;点击【个人】 3.【command shift J】显示所有文件&#xff0c;打开【资源库】 4.删除【Containers】和【Caches】文件 Containers 文件夹&#xff1a;用于存储每个应用程序的沙盒数据&#xff0c;确保应用程序…...

bash+crontab充当半个守护进程的歪招

两个cpolar下的不同程序&#xff0c;都需要定时监测&#xff0c;以免程序没有再运行。有点类似半个守护进程吧。但是守护进程不会写&#xff0c;咋搞&#xff1f;就用这个办法临时当下守门员。这里主要为了备忘xpgrep -各类参数的用法。 #!/bin/bashif pgrep -fl "check_…...

Android JNI的理解与使用。

写在前面&#xff1a;Java相对于C/C来说是更高级的语言&#xff0c;隐藏了指针&#xff0c;可读性更高&#xff0c;更容易学习&#xff0c;但是无法直接操作硬件、运行速度较慢也是不可回避的硬伤。JNI就是Java官方定义的一套标准“接口”&#xff0c;用于Java和C/C之间互相调用…...

解决DeepSeek服务器繁忙的有效方法

全球42%的企业遭遇过AI工具服务器过载导致内容生产中断&#xff08;数据来源&#xff1a;Gartner 2025&#xff09;。当竞品在凌晨3点自动发布「智能家居安装指南」时&#xff0c;你的团队可能正因DeepSeek服务器繁忙错失「净水器保养教程」的流量黄金期⏳。147SEO智能调度系统…...

PyInstaller全平台打包深度解析:资源打包与路径兼容终极方案

一、典型问题场景分析 案例现象&#xff1a;PyQt5开发的GUI程序本地运行正常&#xff0c;但打包后出现以下问题&#xff1a; 程序图标丢失CSS样式表失效图片资源无法加载配置文件读取失败 问题本质&#xff1a;PyInstaller默认不会打包非代码资源文件&#xff0c;且打包后程…...

FreeSwitch的mod_spandsp模块

FreeSWITCH 的 mod_spandsp 模块是一个基于 Spandsp 库 的核心模块&#xff0c;主要用于实现 传真&#xff08;Fax&#xff09;、DTMF 信号处理 以及 语音编解码转换 等功能。它通过集成 SpandSP 的数字信号处理能力&#xff0c;使 FreeSWITCH 能够与传统通信系统&#xff08;如…...

【c++】【Linux】【进程】线程终止/崩溃 会导致进程终止/崩溃 吗?

【c】【Linux】【进程】线程终止/崩溃 会导致进程终止/崩溃 吗&#xff1f; 1.线程终止会导致进程终止吗&#xff1f; 在操作系统中&#xff0c;线程是进程的基本执行单元&#xff0c;一个进程可以包含一个或多个线程。 当一个子线程终止时&#xff0c;进程并不会因此自动终…...

Deepseek 怼CHATGPT实况

提示词&#xff1a; 用犀利的语言批评ChatGPT 深度思考&#xff08;R1) 嗯&#xff0c;用户让我用犀利的语言批评ChatGPT。首先&#xff0c;我需要理解他们的动机。可能他们遇到了ChatGPT的某些问题&#xff0c;比如回答不准确或者缺乏深度&#xff0c;所以想表达不满。也有…...

内核数据结构用法(2)list

list 在 Linux 内核中&#xff0c;链表操作是通过一组宏和函数来实现的&#xff0c;这些操作通常用来管理和遍历链表。以下是一些常用的链表函数和宏的具体用法。 1. 定义链表节点 首先&#xff0c;你需要定义一个包含 struct list_head 的结构体&#xff1a; #include <…...

量化自学 - 金融理论与python - Net Present Value 净现值

净现值&#xff08;Net Present Value&#xff0c;NPV&#xff09;是金融理论中用于评估投资项目价值的指标。其核心思想是将未来现金流按一定贴现率折算为当前价值&#xff0c;并减去初始投资成本。若NPV为正&#xff0c;表明项目预期收益高于成本&#xff0c;具有投资价值&am…...

Java Web开发实战与项目——用户认证与授权模块开发

Web应用中&#xff0c;用户认证与授权是至关重要的功能&#xff0c;确保只有合法用户才能访问受保护的资源。Spring Security作为一个强大的安全框架&#xff0c;支持多种认证与授权方式。在本章节中&#xff0c;我们将深入探讨三种常见的用户认证与授权方案&#xff1a;基于To…...

蓝桥杯篇---IAP15F2K61S2中断

文章目录 前言简介中断源1.外部中断2.定时器中断3.串口中断4.ADC中断5.PCA中断6.SPI中断7.PWM中断 中断优先级中断相关寄存器1.IE2.IP3.TCON4.SCON 中断使用步骤1.配置中断源2.使能中断3.设置优先级4.编写中断服务程序5.清除中断标志 示例代码&#xff1a;外部中断使用示例代码…...

django连接mysql数据库

1.下载mysqlclient第三方库 2.在settings.py里连接数据库&#xff08;提前建好&#xff09; DATABASES {default: {ENGINE: django.db.backends.mysql,NAME: 学生信息,USER: root,PASSWORD: 999123457,HOST: localhost,POST: 3306,} } 3.在models.py里创建一个类&#xff0…...

Python爬虫TLS

TLS指纹校验原理和绕过 浏览器可以正常访问&#xff0c;但是用requests发送请求失败。 后端是如何监测得呢&#xff1f;为什么浏览器可以返回结果&#xff0c;而requests模块不行呢&#xff1f; https://cn.investing.com/equities/amazon-com-inc-historical-data 1.指纹校…...

Docker 部署 MySQL 8 详细图文教程

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall ︱vue3-element-admin︱youlai-boot︱vue-uniapp-template &#x1f33a; 仓库主页&#xff1a; GitCode︱ Gitee ︱ Github &#x1f496; 欢迎点赞 &#x1f44d; 收藏 ⭐评论 …...

基于Python的Diango旅游数据分析推荐系统设计与实现+毕业论文(15000字)

基于Python的Diango旅游数据分析推荐系系统设计与实现毕业论文指导搭建视频&#xff0c;带爬虫 配套论文1w5字 可定制到某个省份&#xff0c;加40 基于用户的协同过滤算法 有后台管理 2w多数据集 可配套指导搭建视频&#xff0c;加20 旅游数据分析推荐系统采用了Python语…...

网络安全java练习平台 js网络安全

&#x1f345; 点击文末小卡片 &#xff0c;免费获取网络安全全套资料&#xff0c;资料在手&#xff0c;涨薪更快 网上学习资料一大堆&#xff0c;但如果学到的知识不成体系&#xff0c;遇到问题时只是浅尝辄止&#xff0c;不再深入研究&#xff0c;那么很难做到真正的技术提升…...

在做题中学习(90):螺旋矩阵II

解法&#xff1a;模拟 思路&#xff1a;创建相同大小的一个二维数组&#xff08;矩阵&#xff09;&#xff0c;用变量标记原矩阵的行数和列数&#xff0c;每次遍历完一行或一列&#xff0c;相应行/列数--&#xff0c;进行对应位置的赋值即可。此题是正方形矩阵&#xff0c;因此…...

Educational Codeforces Round 174 (Rated for Div. 2)(ABCD)

A. Was there an Array? 翻译&#xff1a; 对于整数数组 ​&#xff0c;我们将其相等特征定义为数组 &#xff0c;其中&#xff0c;如果数组 a 的第 i 个元素等于其两个相邻元素&#xff0c;则 &#xff1b;如果数组 a 的第 i 个元素不等于其至少一个相邻元素&#xff0c;则 …...

qemu启动aarch64 linux+ buildroot + 应用程序

1、Linux内核网址 https://www.kernel.org/ 2、安装依赖 sudo apt update sudo apt install -y build-essential qemu qemu-system gcc make bc flex bison libssl-dev libncurses5-dev libelf-dev 3、拉取kernel代码和编译kernel git clone --depth 1 https://git.ker…...

Sponge VS Spring:新兴力量与行业标准的碰撞

框架特性对比 特性SpongeSpring编程语言Go (Golang)Java设计范式低代码, 代码生成, 模块化IoC (控制反转), DI (依赖注入), AOP (面向切面编程)性能高性能, 执行速度快, 并发性好成熟的性能, 需要 JVM 调优, 启动时间可能较长成熟度与稳定性较新, 快速发展中非常成熟, 行业标准…...

推荐几款较好的开源成熟框架

一. 若依&#xff1a; 1. 官方网站&#xff1a;https://doc.ruoyi.vip/ruoyi/ 2. 若依SpringBootVueElement 的后台管理系统&#xff1a;https://gitee.com/y_project/RuoYi-Vue 3. 若依SpringBootVueElement 的后台管理系统&#xff1a;https://gitee.com/y_project/RuoYi-Cl…...

【分布式】Hadoop完全分布式的搭建(零基础)

Hadoop完全分布式的搭建 环境准备&#xff1a; &#xff08;1&#xff09;VMware Workstation Pro17&#xff08;其他也可&#xff09; &#xff08;2&#xff09;Centos7 &#xff08;3&#xff09;FinalShell &#xff08;一&#xff09;模型机配置 0****&#xff09;安…...

JavaScript 异步编程:Promise 与 await 的关联与使用

在 JavaScript 中&#xff0c;异步编程是处理耗时操作&#xff08;如网络请求、文件读写等&#xff09;的核心机制。Promise 和 await 是两种常用的异步编程工具&#xff0c;它们密切相关&#xff0c;但又有各自的特点和适用场景。本文将深入探讨它们的关联、区别以及如何在实际…...

体验用ai做了个python小游戏

体验用ai做了个python小游戏 写在前面使用的工具2.增加功能1.要求增加视频作为背景。2.我让增加了一个欢迎页面。3.我发现中文显示有问题。4.我提出了背景修改意见&#xff0c;欢迎页面和结束页面背景是视频&#xff0c;游戏页面背景是静态图片。5.提出增加更多游戏元素。 总结…...

golang常用库之-swaggo/swag根据注释生成接口文档

文章目录 golang常用库之-swaggo/swag库根据注释生成接口文档什么是swaggo/swag golang常用库之-swaggo/swag库根据注释生成接口文档 什么是swaggo/swag github&#xff1a;https://github.com/swaggo/swag 参考文档&#xff1a;https://golang.halfiisland.com/community/pk…...

【可实战】Linux 常用统计命令:排序sort、去重uniq、统计wc

在 Linux 系统中&#xff0c;有一些常用的命令可以用来收集和统计数据。 一、常用统计命令的使用场景 日志分析和监控&#xff1a;通过使用 Linux 统计命令&#xff0c;可以实时监控和分析系统日志文件&#xff0c;了解系统的运行状况和性能指标。例如&#xff0c;使用 tail 命…...

【设计模式】【创建型模式】建造者模式(Builder)

&#x1f44b;hi&#xff0c;我不是一名外包公司的员工&#xff0c;也不会偷吃茶水间的零食&#xff0c;我的梦想是能写高端CRUD &#x1f525; 2025本人正在沉淀中… 博客更新速度 &#x1f44d; 欢迎点赞、收藏、关注&#xff0c;跟上我的更新节奏 &#x1f3b5; 当你的天空突…...

UE5.3 C++ 通过Spline样条实现三维连线,自己UV贴图。

一.制作了基于USplineComponent的画线插件&#xff0c;就是我们常说的样条线。 直接看怎么用&#xff0c;关于插件实现细节&#xff0c;后续会更新&#xff0c;看思路就行。通过ID,管理每一条线。移除删掉上一帧的线条Mesh。第一个点&#xff0c;是本身直接放过去。第二个点是…...

每日学习Java之一万个为什么

9.Class <?> class1 Myclass.class 为什么要有通配符&#xff1f;传给谁用的&#xff1f; 首先&#xff0c;这里的class特指某个对象在JVM中的元数据集合。 有普通、接口、数组、基本类型、 void 类型、局部类、匿名类、枚举、注解 1.类型安全&#xff1a;通配符允许…...

4、IP查找工具-Angry IP Scanner

在前序文章中&#xff0c;提到了多种IP查找方法&#xff0c;可能回存在不同场景需要使用不同的查找命令&#xff0c;有些不容易记忆&#xff0c;本文将介绍一个比较优秀的IP查找工具&#xff0c;可以应用在连接树莓派或查找IP的其他场景中。供大家参考。 Angry IP Scanner下载…...

华为昇腾920b服务器部署DeepSeek翻车现场

最近到祸一台HUAWEI Kunpeng 920 5250&#xff0c;先看看配置。之前是部署的讯飞大模型&#xff0c;发现资源利用率太低了。把5台减少到3台&#xff0c;就出了他 硬件配置信息 基本硬件信息 按照惯例先来看看配置。一共3块盘&#xff0c;500G的系统盘&#xff0c; 2块3T固态…...

一周学会Flask3 Python Web开发-http响应状态码

锋哥原创的Flask3 Python Web开发 Flask3视频教程&#xff1a; 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 在Flask程序中&#xff0c;客户端发出的请求触发相应的视图函数&#xff0c;获取返回值会作为响应的主体&#xff0c;最后生成…...

Javascript网页设计实例:通过JS实现上传Markdown转化为脑图并下载脑图

功能预览 深度与密度测试 对于测试部分&#xff0c;分别对深度和密度进行了测试&#xff1a; 注意&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;只实现了识别Markdown中的#代表的层级&#xff0c;所以不能使用其余标识符&#xff0…...

【Spring生命周期】Bean元信息配置阶段

引言 本系列将详细讲解Spring生命周期的13个阶段&#xff0c;从源码角度帮助我们更好的理解Spring框架和bean生命周期全流程 Bean信息定义4种方式 API的方式Xml文件方式properties文件的方式注解的方式 在 Spring 框架中&#xff0c;Bean 元信息配置阶段是整个 Bean 生命周…...

【iOS】SwiftUI状态管理

State ObservedObject StateObject 的使用 import SwiftUIclass CountModel: ObservableObject {Published var count: Int 0 // 通过 Published 标记的变量会触发视图更新init() {print("TimerModel initialized at \(count)")} }struct ContentView: View {State…...

ubuntu上如何查看coredump文件默认保存在哪个路径?

在 Ubuntu 系统中&#xff0c;可以通过以下几种方式来查看 coredump 文件默认保存的路径&#xff1a; 1. 查看core_pattern配置 core_pattern是一个内核参数&#xff0c;它决定了 coredump 文件的保存位置和命名规则。可以通过以下命令查看其当前值&#xff1a; cat /proc/s…...

技术总结汇总

目录 数据库 数据库系统原理 MySQL Redis Java Java 基础 Java 容器 Java 并发 Java 虚拟机 Java I/O 系统设计 系统设计基础 微服务 分布式 集群和负载均衡 灾备和故障转移 限流 降级和熔断 缓存 消息队列 设计模式 DDD领域驱动设计 开发框架和中间件…...

Java-如何将其他地方拉取的jar包导入本地maven环境

背景 公司的一个老旧二开项目&#xff0c;原项目维护方不合作了&#xff0c;提供的项目源码提供给到公司。项目中用到了一些原维护方内部的jar包&#xff0c;导致二开时依赖的这些部分全部报错。虽然在项目中直接导入此jar包可以解决报红报错问题&#xff0c;但是在使用maven打…...

Unity 聊天气泡根据文本内容适配

第一步 拼接UI 1、对气泡图进行九宫图切割 2、设置底图pivot位置和对齐方式 pivot位置&#xff1a;&#xff08;0&#xff0c;1&#xff09; 对齐方式&#xff1a;左上对齐 3、设置文本pivot位置和对齐方式&#xff0c;并挂上布局组件 pivot设置和对齐方式和底图一样&#…...

【组态PLC】基于博图V16和组态王六层双部电梯组态设计【含PLC组态源码 M008期】

控制要求 1&#xff09;两台电梯同时运行时&#xff0c;共同享用一套外呼按钮。 2&#xff09;当两台电梯同时去响应外呼信号时&#xff0c;两台电梯自动定向启动前往相应的楼层&#xff0c;当某一台电梯先行到达指定层楼时&#xff0c;另外一台电梯必须就近停靠平层&#xf…...

Python--数据类型(中)

1. 列表&#xff08;list&#xff09; 1.1 定义与特性 定义&#xff1a;有序、可变的容器&#xff0c;支持多种数据类型混合存储。 user_list ["奥力给", 98, True] empty_list [] # 空列表可变性&#xff1a;列表内部元素可修改&#xff0c;区别于字符串和元组。…...

学习总结2.19

首先就是对dfs和bfs的熟悉&#xff0c;dfs是一种递归函数&#xff0c;通过不断深搜来达到目的&#xff0c;通常用于寻找多少未知量&#xff0c;相较于bfs&#xff0c;编译难度更低一点&#xff1b;bfs多用于寻找最短路径之类&#xff0c;相较于dfs代码多了一部分队列的代码&…...

[Vivado报错] [Runs 36-527] DCP does not exist

一、错误原因解析 此错误表明Vivado在指定路径未找到.dcp&#xff08;Design Checkpoint&#xff09;文件&#xff0c;通常由以下原因导致&#xff1a; 路径过长或特殊字符&#xff1a;Windows系统路径长度限制&#xff08;260字符&#xff09;可能导致文件生成失败&#xff…...

深度学习的集装箱箱号OCR识别技术,识别率99.9%

集装箱箱号OCR识别技术是一项结合计算机视觉和规则校验的复杂任务&#xff0c;以下是其关键要点及实现思路的总结&#xff1a; 1、集装箱号结构&#xff1a;11位字符&#xff0c;格式为公司代码(3字母)和序列号(6数字)以及校验码(1数字)和尺寸/类型代码(可选)&#xff0c;例如…...

基于java新闻管理系统,推荐一款开源cms内容管理系统ruoyi-fast-cms

一、项目概述 1.1 项目背景 在信息高速流通的当下&#xff0c;新闻媒体行业每天都要处理和传播海量信息。传统的新闻管理模式依赖人工操作&#xff0c;在新闻采集、编辑、发布以及后续管理等环节中&#xff0c;不仅效率低下&#xff0c;而且容易出现人为失误。同时&#xff0…...