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 | 表示空闲线程的存活时间 | 是 |
TimeUnit | keepAliveTime的单位 | 是 |
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多线程及线程池
线程 一、什么是多线程?二、线程的生命周期三、简单地创建一个线程1、实现Runnable接口2、继承Thread类3、使用Callable和FutureTask4、三种实现方式的对比 四、线程同步和锁1、为什么需要线程同步?2、线程同步的实现方式3、synchronized和ReentrantLock…...
Unreal5从入门到精通之如何在 C++ 中创建 UserWidget
文章目录 前言UUserWidget 子类示例创建我们的 C++ 类的新蓝图子类更改现有蓝图的父类现在我们有了 C++ 基类,下一步做什么?蓝图还是 C++?结论前言 在之前的教程中,我展示了如何在编辑器中创建 UserWidget 蓝图, 在本教程中,我们将创建一个新的基于 C++ 的子类UUserWid…...
Elasticsearch:探索 CLIP 替代方案
作者:来自 Elastic Jeffrey Rengifo 及 Toms Mura 分析图像到图像和文本到图像搜索的 CLIP 模型的替代方案。 在本文中,我们将通过一个模拟房地产网站的实际示例介绍 CLIP 多模态模型,探索替代方案,并分析它们的优缺点,…...
天翼云910B部署DeepSeek蒸馏70B LLaMA模型实践总结
一、项目背景与目标 本文记录在天翼云昇腾910B服务器上部署DeepSeek 70B模型的全过程。该模型是基于LLaMA架构的知识蒸馏版本,模型大小约132GB。 1.1 硬件环境 - 服务器配置:天翼云910B服务器 - NPU:8昇腾910B (每卡64GB显存) - 系统内存&…...
Mac 清理缓存,提高内存空间
步骤 1.打开【访达】 2.菜单栏第五个功能【前往】,点击【个人】 3.【command shift J】显示所有文件,打开【资源库】 4.删除【Containers】和【Caches】文件 Containers 文件夹:用于存储每个应用程序的沙盒数据,确保应用程序…...
bash+crontab充当半个守护进程的歪招
两个cpolar下的不同程序,都需要定时监测,以免程序没有再运行。有点类似半个守护进程吧。但是守护进程不会写,咋搞?就用这个办法临时当下守门员。这里主要为了备忘xpgrep -各类参数的用法。 #!/bin/bashif pgrep -fl "check_…...
Android JNI的理解与使用。
写在前面:Java相对于C/C来说是更高级的语言,隐藏了指针,可读性更高,更容易学习,但是无法直接操作硬件、运行速度较慢也是不可回避的硬伤。JNI就是Java官方定义的一套标准“接口”,用于Java和C/C之间互相调用…...
解决DeepSeek服务器繁忙的有效方法
全球42%的企业遭遇过AI工具服务器过载导致内容生产中断(数据来源:Gartner 2025)。当竞品在凌晨3点自动发布「智能家居安装指南」时,你的团队可能正因DeepSeek服务器繁忙错失「净水器保养教程」的流量黄金期⏳。147SEO智能调度系统…...
PyInstaller全平台打包深度解析:资源打包与路径兼容终极方案
一、典型问题场景分析 案例现象:PyQt5开发的GUI程序本地运行正常,但打包后出现以下问题: 程序图标丢失CSS样式表失效图片资源无法加载配置文件读取失败 问题本质:PyInstaller默认不会打包非代码资源文件,且打包后程…...
FreeSwitch的mod_spandsp模块
FreeSWITCH 的 mod_spandsp 模块是一个基于 Spandsp 库 的核心模块,主要用于实现 传真(Fax)、DTMF 信号处理 以及 语音编解码转换 等功能。它通过集成 SpandSP 的数字信号处理能力,使 FreeSWITCH 能够与传统通信系统(如…...
【c++】【Linux】【进程】线程终止/崩溃 会导致进程终止/崩溃 吗?
【c】【Linux】【进程】线程终止/崩溃 会导致进程终止/崩溃 吗? 1.线程终止会导致进程终止吗? 在操作系统中,线程是进程的基本执行单元,一个进程可以包含一个或多个线程。 当一个子线程终止时,进程并不会因此自动终…...
Deepseek 怼CHATGPT实况
提示词: 用犀利的语言批评ChatGPT 深度思考(R1) 嗯,用户让我用犀利的语言批评ChatGPT。首先,我需要理解他们的动机。可能他们遇到了ChatGPT的某些问题,比如回答不准确或者缺乏深度,所以想表达不满。也有…...
内核数据结构用法(2)list
list 在 Linux 内核中,链表操作是通过一组宏和函数来实现的,这些操作通常用来管理和遍历链表。以下是一些常用的链表函数和宏的具体用法。 1. 定义链表节点 首先,你需要定义一个包含 struct list_head 的结构体: #include <…...
量化自学 - 金融理论与python - Net Present Value 净现值
净现值(Net Present Value,NPV)是金融理论中用于评估投资项目价值的指标。其核心思想是将未来现金流按一定贴现率折算为当前价值,并减去初始投资成本。若NPV为正,表明项目预期收益高于成本,具有投资价值&am…...
Java Web开发实战与项目——用户认证与授权模块开发
Web应用中,用户认证与授权是至关重要的功能,确保只有合法用户才能访问受保护的资源。Spring Security作为一个强大的安全框架,支持多种认证与授权方式。在本章节中,我们将深入探讨三种常见的用户认证与授权方案:基于To…...
蓝桥杯篇---IAP15F2K61S2中断
文章目录 前言简介中断源1.外部中断2.定时器中断3.串口中断4.ADC中断5.PCA中断6.SPI中断7.PWM中断 中断优先级中断相关寄存器1.IE2.IP3.TCON4.SCON 中断使用步骤1.配置中断源2.使能中断3.设置优先级4.编写中断服务程序5.清除中断标志 示例代码:外部中断使用示例代码…...
django连接mysql数据库
1.下载mysqlclient第三方库 2.在settings.py里连接数据库(提前建好) DATABASES {default: {ENGINE: django.db.backends.mysql,NAME: 学生信息,USER: root,PASSWORD: 999123457,HOST: localhost,POST: 3306,} } 3.在models.py里创建一个类࿰…...
Python爬虫TLS
TLS指纹校验原理和绕过 浏览器可以正常访问,但是用requests发送请求失败。 后端是如何监测得呢?为什么浏览器可以返回结果,而requests模块不行呢? https://cn.investing.com/equities/amazon-com-inc-historical-data 1.指纹校…...
Docker 部署 MySQL 8 详细图文教程
🚀 作者主页: 有来技术 🔥 开源项目: youlai-mall ︱vue3-element-admin︱youlai-boot︱vue-uniapp-template 🌺 仓库主页: GitCode︱ Gitee ︱ Github 💖 欢迎点赞 👍 收藏 ⭐评论 …...
基于Python的Diango旅游数据分析推荐系统设计与实现+毕业论文(15000字)
基于Python的Diango旅游数据分析推荐系系统设计与实现毕业论文指导搭建视频,带爬虫 配套论文1w5字 可定制到某个省份,加40 基于用户的协同过滤算法 有后台管理 2w多数据集 可配套指导搭建视频,加20 旅游数据分析推荐系统采用了Python语…...
网络安全java练习平台 js网络安全
🍅 点击文末小卡片 ,免费获取网络安全全套资料,资料在手,涨薪更快 网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升…...
在做题中学习(90):螺旋矩阵II
解法:模拟 思路:创建相同大小的一个二维数组(矩阵),用变量标记原矩阵的行数和列数,每次遍历完一行或一列,相应行/列数--,进行对应位置的赋值即可。此题是正方形矩阵,因此…...
Educational Codeforces Round 174 (Rated for Div. 2)(ABCD)
A. Was there an Array? 翻译: 对于整数数组 ,我们将其相等特征定义为数组 ,其中,如果数组 a 的第 i 个元素等于其两个相邻元素,则 ;如果数组 a 的第 i 个元素不等于其至少一个相邻元素,则 …...
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 调优, 启动时间可能较长成熟度与稳定性较新, 快速发展中非常成熟, 行业标准…...
推荐几款较好的开源成熟框架
一. 若依: 1. 官方网站:https://doc.ruoyi.vip/ruoyi/ 2. 若依SpringBootVueElement 的后台管理系统:https://gitee.com/y_project/RuoYi-Vue 3. 若依SpringBootVueElement 的后台管理系统:https://gitee.com/y_project/RuoYi-Cl…...
【分布式】Hadoop完全分布式的搭建(零基础)
Hadoop完全分布式的搭建 环境准备: (1)VMware Workstation Pro17(其他也可) (2)Centos7 (3)FinalShell (一)模型机配置 0****)安…...
JavaScript 异步编程:Promise 与 await 的关联与使用
在 JavaScript 中,异步编程是处理耗时操作(如网络请求、文件读写等)的核心机制。Promise 和 await 是两种常用的异步编程工具,它们密切相关,但又有各自的特点和适用场景。本文将深入探讨它们的关联、区别以及如何在实际…...
体验用ai做了个python小游戏
体验用ai做了个python小游戏 写在前面使用的工具2.增加功能1.要求增加视频作为背景。2.我让增加了一个欢迎页面。3.我发现中文显示有问题。4.我提出了背景修改意见,欢迎页面和结束页面背景是视频,游戏页面背景是静态图片。5.提出增加更多游戏元素。 总结…...
golang常用库之-swaggo/swag根据注释生成接口文档
文章目录 golang常用库之-swaggo/swag库根据注释生成接口文档什么是swaggo/swag golang常用库之-swaggo/swag库根据注释生成接口文档 什么是swaggo/swag github:https://github.com/swaggo/swag 参考文档:https://golang.halfiisland.com/community/pk…...
【可实战】Linux 常用统计命令:排序sort、去重uniq、统计wc
在 Linux 系统中,有一些常用的命令可以用来收集和统计数据。 一、常用统计命令的使用场景 日志分析和监控:通过使用 Linux 统计命令,可以实时监控和分析系统日志文件,了解系统的运行状况和性能指标。例如,使用 tail 命…...
【设计模式】【创建型模式】建造者模式(Builder)
👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD 🔥 2025本人正在沉淀中… 博客更新速度 👍 欢迎点赞、收藏、关注,跟上我的更新节奏 🎵 当你的天空突…...
UE5.3 C++ 通过Spline样条实现三维连线,自己UV贴图。
一.制作了基于USplineComponent的画线插件,就是我们常说的样条线。 直接看怎么用,关于插件实现细节,后续会更新,看思路就行。通过ID,管理每一条线。移除删掉上一帧的线条Mesh。第一个点,是本身直接放过去。第二个点是…...
每日学习Java之一万个为什么
9.Class <?> class1 Myclass.class 为什么要有通配符?传给谁用的? 首先,这里的class特指某个对象在JVM中的元数据集合。 有普通、接口、数组、基本类型、 void 类型、局部类、匿名类、枚举、注解 1.类型安全:通配符允许…...
4、IP查找工具-Angry IP Scanner
在前序文章中,提到了多种IP查找方法,可能回存在不同场景需要使用不同的查找命令,有些不容易记忆,本文将介绍一个比较优秀的IP查找工具,可以应用在连接树莓派或查找IP的其他场景中。供大家参考。 Angry IP Scanner下载…...
华为昇腾920b服务器部署DeepSeek翻车现场
最近到祸一台HUAWEI Kunpeng 920 5250,先看看配置。之前是部署的讯飞大模型,发现资源利用率太低了。把5台减少到3台,就出了他 硬件配置信息 基本硬件信息 按照惯例先来看看配置。一共3块盘,500G的系统盘, 2块3T固态…...
一周学会Flask3 Python Web开发-http响应状态码
锋哥原创的Flask3 Python Web开发 Flask3视频教程: 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 在Flask程序中,客户端发出的请求触发相应的视图函数,获取返回值会作为响应的主体,最后生成…...
Javascript网页设计实例:通过JS实现上传Markdown转化为脑图并下载脑图
功能预览 深度与密度测试 对于测试部分,分别对深度和密度进行了测试: 注意!!!!!!!只实现了识别Markdown中的#代表的层级,所以不能使用其余标识符࿰…...
【Spring生命周期】Bean元信息配置阶段
引言 本系列将详细讲解Spring生命周期的13个阶段,从源码角度帮助我们更好的理解Spring框架和bean生命周期全流程 Bean信息定义4种方式 API的方式Xml文件方式properties文件的方式注解的方式 在 Spring 框架中,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 系统中,可以通过以下几种方式来查看 coredump 文件默认保存的路径: 1. 查看core_pattern配置 core_pattern是一个内核参数,它决定了 coredump 文件的保存位置和命名规则。可以通过以下命令查看其当前值: cat /proc/s…...
技术总结汇总
目录 数据库 数据库系统原理 MySQL Redis Java Java 基础 Java 容器 Java 并发 Java 虚拟机 Java I/O 系统设计 系统设计基础 微服务 分布式 集群和负载均衡 灾备和故障转移 限流 降级和熔断 缓存 消息队列 设计模式 DDD领域驱动设计 开发框架和中间件…...
Java-如何将其他地方拉取的jar包导入本地maven环境
背景 公司的一个老旧二开项目,原项目维护方不合作了,提供的项目源码提供给到公司。项目中用到了一些原维护方内部的jar包,导致二开时依赖的这些部分全部报错。虽然在项目中直接导入此jar包可以解决报红报错问题,但是在使用maven打…...
Unity 聊天气泡根据文本内容适配
第一步 拼接UI 1、对气泡图进行九宫图切割 2、设置底图pivot位置和对齐方式 pivot位置:(0,1) 对齐方式:左上对齐 3、设置文本pivot位置和对齐方式,并挂上布局组件 pivot设置和对齐方式和底图一样&#…...
【组态PLC】基于博图V16和组态王六层双部电梯组态设计【含PLC组态源码 M008期】
控制要求 1)两台电梯同时运行时,共同享用一套外呼按钮。 2)当两台电梯同时去响应外呼信号时,两台电梯自动定向启动前往相应的楼层,当某一台电梯先行到达指定层楼时,另外一台电梯必须就近停靠平层…...
Python--数据类型(中)
1. 列表(list) 1.1 定义与特性 定义:有序、可变的容器,支持多种数据类型混合存储。 user_list ["奥力给", 98, True] empty_list [] # 空列表可变性:列表内部元素可修改,区别于字符串和元组。…...
学习总结2.19
首先就是对dfs和bfs的熟悉,dfs是一种递归函数,通过不断深搜来达到目的,通常用于寻找多少未知量,相较于bfs,编译难度更低一点;bfs多用于寻找最短路径之类,相较于dfs代码多了一部分队列的代码&…...
[Vivado报错] [Runs 36-527] DCP does not exist
一、错误原因解析 此错误表明Vivado在指定路径未找到.dcp(Design Checkpoint)文件,通常由以下原因导致: 路径过长或特殊字符:Windows系统路径长度限制(260字符)可能导致文件生成失败ÿ…...
深度学习的集装箱箱号OCR识别技术,识别率99.9%
集装箱箱号OCR识别技术是一项结合计算机视觉和规则校验的复杂任务,以下是其关键要点及实现思路的总结: 1、集装箱号结构:11位字符,格式为公司代码(3字母)和序列号(6数字)以及校验码(1数字)和尺寸/类型代码(可选),例如…...
基于java新闻管理系统,推荐一款开源cms内容管理系统ruoyi-fast-cms
一、项目概述 1.1 项目背景 在信息高速流通的当下,新闻媒体行业每天都要处理和传播海量信息。传统的新闻管理模式依赖人工操作,在新闻采集、编辑、发布以及后续管理等环节中,不仅效率低下,而且容易出现人为失误。同时࿰…...