Java线程池:深入理解与高效应用

引言

在现代软件开发中,多线程编程已成为提高应用性能的关键技术之一。Java线程池作为管理线程的一种高效机制,允许我们复用线程,减少线程创建和销毁的开销,并且可以有效地控制并发线程的数量,避免资源耗尽。本专栏旨在深入探讨Java线程池的内部机制、配置方法以及在实际开发中的应用。

第一部分:线程池基础

1. 线程池概述

在多线程编程中,线程的创建和销毁是一个相对昂贵的操作,因为它涉及到操作系统层面的资源分配。线程池提供了一种解决方案,通过复用一组有限的线程来执行多个任务,从而减少了线程创建和销毁的开销。线程池还有助于防止系统过载,因为它可以限制同时运行的线程数量。

线程池的主要优点包括:

  • 资源优化:减少频繁创建和销毁线程的开销。
  • 提高响应速度:线程可以快速从任务队列中获取任务并执行。
  • 提高线程的可管理性:统一管理线程的创建、执行和销毁。
  • 控制并发级别:避免过多的线程导致系统过载。

2. Java线程池的实现

在Java中,线程池的实现主要依赖于java.util.concurrent包中的几个关键类。以下是一些核心类及其作用:

  • Executor:一个接口,定义了执行提交的Runnable任务的方法。
  • Executors:一个工厂类,提供了一些静态方法来创建不同类型的线程池。
  • ExecutorService:一个接口,扩展了Executor接口,并提供了额外的管理任务生命周期的方法,如shutdown()awaitTermination()
  • ThreadPoolExecutorExecutorService的一个实现,提供了更细粒度的线程池控制。

3. 线程池参数

线程池的性能和行为可以通过几个关键参数来配置:

  • 核心线程数(corePoolSize:线程池中始终保持的线程数量,即使它们处于空闲状态。
  • 最大线程数(maximumPoolSize:线程池中允许的最大线程数量。如果任务太多,超出核心线程数的线程会在任务队列中等待。
  • 队列容量(workQueue:一个阻塞队列,用于存放等待执行的任务。队列的容量决定了能容纳多少任务。
  • 线程存活时间(keepAliveTime:当线程池中正在运行的线程数量超过核心线程数时,多余的空闲线程能等待新任务的最长时间。
  • 拒绝策略(RejectedExecutionHandler:当任务太多而不能分配线程时,定义了任务将如何被处理。Java提供了几种内置的拒绝策略,如AbortPolicyCallerRunsPolicyDiscardPolicyDiscardOldestPolicy
示例代码

以下是使用ThreadPoolExecutor构造函数创建线程池的一个示例:

int corePoolSize = 5;
int maximumPoolSize = 10;
long keepAliveTime = 1L;
TimeUnit unit = TimeUnit.MINUTES;
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(10);
ThreadFactory threadFactory = Executors.defaultThreadFactory();
RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,threadFactory,handler
);

在这个示例中,我们创建了一个核心线程数为5,最大线程数为10,空闲线程存活时间为1分钟的线程池。任务队列的容量为10,如果提交的任务超过队列容量,将使用AbortPolicy拒绝策略。

通过深入理解线程池的这些基础概念和参数,开发者可以更有效地利用线程池来优化多线程应用的性能。在下一部分中,我们将探讨线程池的工作原理,包括线程池的生命周期和任务的提交与执行机制。

第二部分:线程池的工作原理

1. 线程池的生命周期

线程池的生命周期管理是确保资源合理利用和系统稳定性的关键。线程池的生命周期通常包括以下几个阶段:

  • 初始化(Running):线程池创建后,默认处于运行状态,可以接受新任务并执行。
  • 关闭(Shutting down):调用shutdown()方法后,线程池不会接受新任务,但会完成执行队列中已有的任务。
  • 终止(Terminated):当所有任务都已执行完毕,线程池会进入终止状态。此时,调用shutdownNow()方法会尝试停止所有正在执行的任务,并返回等待执行的任务列表。
  • 等待终止(Awaiting termination):在调用awaitTermination()方法后,当前线程会等待直到线程池完全关闭。
示例代码

以下是如何管理线程池生命周期的示例:

// 创建线程池
ExecutorService executor = Executors.newFixedThreadPool(5);// 关闭线程池,不再接受新任务
executor.shutdown();// 尝试停止所有正在执行的任务,并返回未执行的任务列表
List<Runnable> droppedTasks = executor.shutdownNow();// 等待线程池中的所有任务执行完成
executor.awaitTermination(60, TimeUnit.SECONDS);

2. 任务提交与执行

任务提交到线程池后,线程池会根据当前的运行状态和队列情况来决定如何执行这些任务。任务可以通过两种方式提交:

  • execute(Runnable):提交一个Runnable任务,没有返回值。
  • submit(Runnable)submit(Callable):提交一个RunnableCallable任务,并返回一个Future对象,可以通过这个对象来查询任务执行状态、取消任务或获取任务执行结果。
示例代码

以下是如何提交任务到线程池的示例:

// 创建线程池
ExecutorService executor = Executors.newFixedThreadPool(5);// 提交Runnable任务
executor.execute(new Runnable() {public void run() {System.out.println("任务开始执行");// 任务逻辑System.out.println("任务执行完成");}
});// 提交Callable任务,并获取Future对象
Future<Integer> future = executor.submit(new Callable<Integer>() {public Integer call() throws Exception {// 执行一些计算任务return 123;}
});// 获取任务结果
try {Integer result = future.get(); // 等待任务完成并获取结果System.out.println("任务返回结果: " + result);
} catch (InterruptedException | ExecutionException e) {e.printStackTrace();
}

3. 任务队列

任务队列是线程池中用于存放等待执行任务的集合。Java提供了多种阻塞队列实现,每种队列都有其特点:

  • ArrayBlockingQueue:基于数组的有界阻塞队列。
  • LinkedBlockingQueue:基于链表的可选有界阻塞队列。
  • SynchronousQueue:一个不存储元素的阻塞队列,每个插入操作必须等待一个相应的移除操作。
  • PriorityBlockingQueue:基于优先级的无界阻塞队列。

选择合适的任务队列对于优化线程池的性能至关重要。

示例代码

以下是如何使用不同类型的阻塞队列的示例:

// 使用ArrayBlockingQueue创建有界任务队列
BlockingQueue<Runnable> queue1 = new ArrayBlockingQueue<>(10);// 使用LinkedBlockingQueue创建无界任务队列
BlockingQueue<Runnable> queue2 = new LinkedBlockingQueue<>();// 使用SynchronousQueue创建不存储元素的任务队列
BlockingQueue<Runnable> queue3 = new SynchronousQueue<>();// 使用PriorityBlockingQueue创建优先级任务队列
BlockingQueue<Runnable> queue4 = new PriorityBlockingQueue<>();// 创建线程池时指定任务队列
ExecutorService executor1 = new ThreadPoolExecutor(5, 10, 1L, TimeUnit.MINUTES, queue1);
ExecutorService executor2 = new ThreadPoolExecutor(5, 10, 1L, TimeUnit.MINUTES, queue2);
ExecutorService executor3 = new ThreadPoolExecutor(5, 10, 1L, TimeUnit.MINUTES, queue3);
ExecutorService executor4 = new ThreadPoolExecutor(5, 10, 1L, TimeUnit.MINUTES, queue4);

第三部分:线程池的配置与使用

1. 固定线程池

固定线程池是最常见的线程池类型,它允许您指定核心线程数和最大线程数。核心线程始终存活,除非设置allowCoreThreadTimeOuttrue。非核心线程在空闲时会被终止。

示例代码

int corePoolSize = 5;
int maximumPoolSize = 5;
long keepAliveTime = 1;
TimeUnit unit = TimeUnit.MINUTES;
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>();ThreadPoolExecutor fixedThreadPool = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue
);

2. 缓存线程池

缓存线程池允许您创建一个可根据需要创建新线程的线程池,核心线程数为0,最大线程数为Integer.MAX_VALUE。它使用SynchronousQueue作为其任务队列。

示例代码

int corePoolSize = 0;
long keepAliveTime = 60;
TimeUnit unit = TimeUnit.SECONDS;
BlockingQueue<Runnable> workQueue = new SynchronousQueue<>();ThreadPoolExecutor cachedThreadPool = new ThreadPoolExecutor(corePoolSize,Integer.MAX_VALUE,keepAliveTime,unit,workQueue
);

3. 单线程池

单线程池确保所有的任务都在同一个线程中按顺序执行,适用于任务之间存在依赖关系的场景。

示例代码

int corePoolSize = 1;
int maximumPoolSize = 1;
long keepAliveTime = 0;
TimeUnit unit = TimeUnit.MILLISECONDS;
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>();ThreadPoolExecutor singleThreadExecutor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue
);

4. 调度线程池

调度线程池允许您安排任务在将来的某个时间点执行,或者定期执行。

示例代码

ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);// 延迟执行
scheduledExecutorService.schedule(() -> System.out.println("延迟任务执行"),10, // 延迟时间,单位为秒TimeUnit.SECONDS
);// 周期性执行
scheduledExecutorService.scheduleAtFixedRate(() -> System.out.println("周期性任务执行"),0, // 初始延迟时间5, // 执行间隔时间,单位为秒TimeUnit.SECONDS
);

5. 自定义线程池

自定义线程池允许您通过实现ThreadFactoryRejectedExecutionHandler接口来定制线程的创建和任务的拒绝策略。

示例代码

int corePoolSize = 5;
int maximumPoolSize = 10;
long keepAliveTime = 2;
TimeUnit unit = TimeUnit.SECONDS;
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(10);ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("custom-thread-pool-%d").build();RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();ThreadPoolExecutor customThreadPool = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,threadFactory,handler
);

6. 线程池的合理关闭

合理关闭线程池可以释放系统资源,防止内存泄漏。应避免在应用程序运行时突然关闭线程池。

示例代码

ExecutorService executorService = Executors.newFixedThreadPool(5);// 关闭线程池
executorService.shutdown();try {// 等待线程池关闭if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {// 取消所有未执行的任务List<Runnable> droppedTasks = executorService.shutdownNow();System.out.println("取消的任务数: " + droppedTasks.size());}
} catch (InterruptedException e) {// 当前线程被中断时的处理executorService.shutdownNow();Thread.currentThread().interrupt();
}

通过以上示例,我们可以看到Java线程池提供了丰富的配置选项,允许开发者根据不同的应用场景和需求来定制线程池的行为。在下一部分中,我们将探讨如何对线程池进行性能优化,包括监控、参数调优以及避免常见的并发问题。

第四部分:线程池的性能优化

1. 线程池监控

监控线程池的状态对于性能优化至关重要。通过监控,我们可以了解线程池的运行情况,包括活动线程数、任务队列大小、完成的任务数等。

示例代码

以下是如何使用ThreadPoolExecutorgetActiveCount()getTaskCount()方法来监控线程池状态的示例:

ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(5);// 获取活动线程数
int activeCount = executor.getActiveCount();
System.out.println("活动线程数: " + activeCount);// 获取已完成任务数
long completedTaskCount = executor.getCompletedTaskCount();
System.out.println("已完成的任务数: " + completedTaskCount);

2. 线程池调优

线程池的参数调优可以显著提高应用程序的性能。合理的参数配置可以减少延迟、避免过载,并提高资源利用率。

示例代码

以下是如何根据工作负载动态调整线程池大小的示例:

ThreadPoolExecutor executor = new ThreadPoolExecutor(5, // corePoolSize10, // maximumPoolSize1, // keepAliveTimeTimeUnit.MINUTES,new LinkedBlockingQueue<Runnable>()
);// 根据当前工作负载调整线程池大小
int newCorePoolSize = calculateNewCorePoolSize();
executor.setCorePoolSize(newCorePoolSize);

其中calculateNewCorePoolSize()是一个自定义方法,用于根据当前系统状态和工作负载动态计算新的corePoolSize

3. 避免死锁和资源竞争

死锁和资源竞争是多线程编程中常见的问题。通过合理的设计和使用同步机制,可以避免这些问题。

示例代码

以下是使用ConcurrentHashMap避免资源竞争的示例:

ConcurrentHashMap<Integer, String> map = new ConcurrentHashMap<>();// 线程安全地更新map
map.compute(key, (k, v) -> {// 执行一些操作return newValue;
});

4. 线程池的合理关闭

合理关闭线程池可以释放系统资源,防止内存泄漏。应避免在应用程序运行时突然关闭线程池,这可能会导致正在执行的任务被中断。

示例代码

以下是如何合理关闭线程池的示例:

ExecutorService executor = Executors.newFixedThreadPool(5);// 在适当的时候关闭线程池
executor.shutdown(); // 拒绝新任务,但允许现有任务完成try {// 可选地,等待所有任务完成if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {executor.shutdownNow(); // 尝试停止所有正在执行的任务}
} catch (InterruptedException e) {// 当前线程被中断时的处理executor.shutdownNow();
}

5. 使用线程池执行定时任务和周期性任务

调度线程池(ScheduledExecutorService)允许您安排任务在将来的某个时间点执行,或者定期执行。

示例代码

以下是如何使用ScheduledExecutorService安排周期性任务的示例:

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);// 周期性执行任务,初始延迟1秒,之后每隔2秒执行一次
scheduler.scheduleAtFixedRate(new Runnable() {public void run() {System.out.println("周期性任务执行");}
}, 1, 2, TimeUnit.SECONDS);

6. 线程池的拒绝策略

当任务太多而不能分配线程时,线程池的拒绝策略定义了如何处理额外的任务。Java提供了几种内置的拒绝策略,也可以自定义拒绝策略。

示例代码

以下是如何自定义拒绝策略的示例:

RejectedExecutionHandler customHandler = new RejectedExecutionHandler() {public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {// 任务被拒绝时的处理逻辑System.out.println("任务 " + r.toString() + " 被拒绝");}
};ThreadPoolExecutor executor = new ThreadPoolExecutor(5, // corePoolSize10, // maximumPoolSize1, // keepAliveTimeTimeUnit.MINUTES,new LinkedBlockingQueue<Runnable>(),customHandler
);

通过以上示例,我们可以看到线程池的性能优化是一个多方面的过程,涉及到监控、参数调优、避免死锁和资源竞争、合理关闭线程池以及使用合适的拒绝策略。正确实施这些策略可以显著提高应用程序的性能和稳定性。在下一部分中,我们将通过案例分析来进一步探讨线程池的实际应用。

第五部分:线程池的高级应用

1. 自定义线程池

自定义线程池允许开发者根据特定需求调整线程创建逻辑、任务执行方式和线程池管理策略。通过实现ThreadFactoryRejectedExecutionHandler接口,可以创建具有特定行为的线程池。

示例代码

以下是如何自定义线程名称和拒绝策略的示例:

int corePoolSize = 5;
int maximumPoolSize = 10;
long keepAliveTime = 1L;
TimeUnit unit = TimeUnit.MINUTES;
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>();ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("custom-pool-%d").build();RejectedExecutionHandler customHandler = new ThreadPoolExecutor.CallerRunsPolicy();ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,namedThreadFactory,customHandler
);

2. 线程池与并发工具类

Java并发API提供了多种工具类,如FutureCallableExecutors等,它们与线程池结合使用可以提供更强大的并发控制能力。

示例代码

以下是如何使用Future获取任务执行结果的示例:

ExecutorService executor = Executors.newFixedThreadPool(5);Future<Integer> future = executor.submit(() -> {// 执行一些计算任务return 123;
});try {Integer result = future.get(); // 等待任务完成并获取结果System.out.println("任务返回结果: " + result);
} catch (InterruptedException | ExecutionException e) {e.printStackTrace();
}

3. 线程池与Java 8新特性

Java 8的Lambda表达式和Stream API可以与线程池结合,简化并发编程的复杂性。

示例代码

以下是如何使用Lambda表达式和线程池并行处理集合数据的示例:

ExecutorService executor = Executors.newFixedThreadPool(5);List<String> strings = Arrays.asList("a1", "a2", "b1", "c2", "d3");strings.parallelStream().forEach(s -> {executor.submit(() -> {// 对字符串进行处理System.out.println("处理: " + s);});
});

4. 线程池的优雅关闭

在应用关闭时,需要优雅地关闭线程池,确保所有任务都能完成执行,避免资源泄露。

示例代码

以下是如何优雅关闭线程池的示例:

ExecutorService executor = Executors.newFixedThreadPool(5);// 在应用关闭时
executor.shutdown(); // 启动关闭序列,不再接受新任务try {if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {executor.shutdownNow(); // 尝试立即停止所有正在执行的任务}
} catch (InterruptedException e) {executor.shutdownNow(); // 当前线程被中断时的处理
}

5. 线程池的监控和管理

使用ManagementFactoryThreadMXBean可以监控线程池的运行状态,包括线程的CPU时间、阻塞时间等。

示例代码

以下是如何监控线程池中线程的运行状态的示例:

ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(5);
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();for (ThreadInfo threadInfo : threadMXBean.dumpAllThreads(true, true)) {if (threadInfo.getThreadName().startsWith("custom-pool")) {System.out.println("线程名: " + threadInfo.getThreadName());System.out.println("线程状态: " + threadInfo.getThreadState());}
}

6. 线程池的动态调整

根据系统负载动态调整线程池大小,可以提高资源利用率并优化性能。

示例代码

以下是如何根据任务队列长度动态调整线程池大小的示例:

ThreadPoolExecutor executor = new ThreadPoolExecutor(5, // corePoolSize10, // maximumPoolSize1L, // keepAliveTimeTimeUnit.MINUTES,new LinkedBlockingQueue<Runnable>()
);int queueSize = ((BlockingQueue<Runnable>) executor.getQueue()).size();
if (queueSize > 50) {executor.setMaximumPoolSize(15); // 如果队列中的任务超过50个,增加最大线程数
} else if (queueSize < 10) {executor.setMaximumPoolSize(5); // 如果队列中的任务少于10个,减少最大线程数
}

通过以上示例,我们可以看到线程池的高级应用涉及到自定义线程池、与并发工具类的结合使用、利用Java 8新特性简化并发编程、优雅关闭线程池、监控和管理线程池以及根据系统负载动态调整线程池大小。这些高级应用可以帮助开发者更好地利用线程池,提高应用程序的性能和稳定性。

第六部分:案例分析

1. 真实场景案例分析

案例一:Web服务器请求处理

在Web服务器中,线程池常用于处理并发的HTTP请求。通过合理配置线程池,可以提高服务器的响应速度和吞吐量。

示例代码

// 创建一个固定大小的线程池
int numThreads = Runtime.getRuntime().availableProcessors();
ExecutorService executor = Executors.newFixedThreadPool(numThreads);// 模拟接收HTTP请求
for (int i = 0; i < 100; i++) {final int requestId = i;executor.submit(() -> {try {// 模拟请求处理System.out.println("处理请求: " + requestId);Thread.sleep(1000); // 模拟请求处理时间System.out.println("请求: " + requestId + " 处理完成");} catch (InterruptedException e) {Thread.currentThread().interrupt();}});
}
案例二:批量数据处理

在批量数据处理场景中,线程池可以用来并行处理数据集,从而加快处理速度。

示例代码

// 假设有一个大数据集需要处理
List<Data> dataList = fetchDataSet();// 创建一个固定大小的线程池
ExecutorService executor = Executors.newFixedThreadPool(5);// 将数据集分割成多个批次,并分配给线程池中的线程
int batchSize = dataList.size() / 5;
for (int i = 0; i < dataList.size(); i += batchSize) {final List<Data> batch = dataList.subList(i, Math.min(i + batchSize, dataList.size()));executor.submit(() -> {processBatch(batch); // 处理数据批次});
}// 定义数据处理方法
void processBatch(List<Data> batch) {for (Data data : batch) {// 执行数据处理逻辑}
}

2. 问题诊断与解决

常见问题一:线程池拒绝任务

当线程池达到其最大容量,且任务队列已满时,新提交的任务可能会被拒绝。

解决策略

  • 增加线程池的核心线程数或最大线程数。
  • 增大任务队列的容量。
  • 实现自定义拒绝策略,例如记录日志或将任务存储到其他存储中。

示例代码

RejectedExecutionHandler customHandler = new RejectedExecutionHandler() {public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {System.out.println("任务 " + r.toString() + " 被拒绝,考虑增加线程池大小或队列容量。");// 这里可以添加日志记录或其他处理逻辑}
};
常见问题二:线程池中的线程饥饿

如果线程池中的线程长时间得不到任务执行,可能会导致线程饥饿。

解决策略

  • 调整线程池的keepAliveTime参数,使空闲线程能够更及时地终止。
  • 使用动态线程池,根据当前任务负载动态调整线程数量。

示例代码

// 创建一个动态调整大小的线程池
int corePoolSize = 5;
int maximumPoolSize = 10;
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>();
ThreadPoolExecutor dynamicExecutor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,60L,TimeUnit.SECONDS,workQueue
);// 根据需要调整线程池大小
adjustPoolSizeBasedOnLoad(dynamicExecutor);
常见问题三:线程池中的线程死锁

线程池中的线程可能会因为资源竞争或不正确的同步操作而发生死锁。

解决策略

  • 避免在线程池中执行需要长时间持有多个锁的操作。
  • 使用Concurrent包中的线程安全集合,减少锁的使用。

示例代码

// 使用ConcurrentHashMap避免死锁
ConcurrentHashMap<String, Data> map = new ConcurrentHashMap<>();executor.submit(() -> {String key = "someKey";Data data = map.computeIfAbsent(key, k -> {// 计算并返回数据return fetchData(k);});// 处理数据
});

通过以上案例分析和问题诊断,我们可以看到线程池在实际应用中可能遇到的问题以及相应的解决策略。理解这些问题和策略有助于开发者更有效地使用线程池,提高应用程序的性能和稳定性。

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

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

相关文章

解决VIvado编程中遇到的bug 5

解决VIvado编程中遇到的bug 5 语言 &#xff1a;Verilg HDL EDA工具&#xff1a; Vivado、quartus2 、modelsim 解决VIvado编程中遇到的bug 5一、引言二、问题、分析及解决方法1. vivado编译时报错&#xff08;1&#xff09;错误&#xff08;2&#xff09;分析&#xff08;3&am…

YOLOv8 多种任务网络结构详细解析 | 目标检测、实例分割、人体关键点检测、图像分类

前言 本文仅根据模型的预测过程&#xff0c;即从输入图像到输出结果&#xff08;图像预处理、模型推理、后处理&#xff09;&#xff0c;来展现不同任务下的网络结构&#xff0c;OBB 任务暂不包含。 Backbone 1. yolov8m 2. yolov8m-p2 3. yolov8m-p6 4. 细节 图中 CBS Con…

DALL·E 2详解:人工智能如何将您的想象力变为现实!

引言 DALLE 2是一个基于人工智能的图像生成模型&#xff0c;它通过理解自然语言描述来生成匹配这些描述的图像。这一模型的核心在于其创新的两阶段工作流程&#xff0c;首先是将文本描述转换为图像表示&#xff0c;然后是基于这个表示生成具体的图像。 下面详细介绍DALL-E2的功…

Java高级---Spring Boot---3快速入门

3 Spring Boot快速入门 3.1 创建第一个Spring Boot项目 使用IntelliJ IDEA和Spring Initializr创建项目 打开IntelliJ IDEA 并选择 “Start a new project”。在新建项目向导中&#xff0c;选择 “Spring Initializr”。填写项目基本信息&#xff1a; Group: 定义项目组&…

C#使用GDI对一个矩形进行任意角度旋转

C#对一个矩形进行旋转GDI绘图&#xff0c;可以指定任意角度进行旋转 我们可以认为一张图片Image&#xff0c;本质就是一个矩形Rectangle,旋转矩形也就是旋转图片 在画图密封类 System.Drawing.Graphics中&#xff0c; 矩形旋转的两个关键方法 //设置旋转的中心点 public v…

如何设计创新型薪酬福利体系?

薪酬福利体系是企业吸引和留住人才的重要工具&#xff0c;然而&#xff0c;传统的薪酬福利体系缺少一定的灵活性&#xff0c;同时在满足员工多样性需求方面也比较欠缺。所以越来越多的企业选择建立创新型的薪酬福利体系。那么应该如何建立兼具灵活和激励的创新型的薪酬福利体系…

sns报错 UserWarning: Dataset has 0 variance; skipping density estimate.

报错信息 UserWarning: Dataset has 0 variance; skipping density estimate. Pass warn_singularFalse to disable this warning. 三维数组维度(1000, 27,1)&#xff0c;取出第一个元素画出分布图保存检查错误原因&#xff0c;print(Mcases[:,:,0].shape) 显示维度(1000, 27)修…

生成随机图片

package com.zhuguohui.app.lib.tools;/*** Created by zhuguohui* Date: 2024/6/1* Time: 13:39* Desc:获取随机图片*/ public class RandomImage {// static final String url "https://picsum.photos/%d/%d?random%d";static final String url "https://…

FPGA定点数FFT过后转换为浮点数与Matlab计算的FFT结果进行比对

目录 1.前言2.FPGA的testbench中如何读取数据文件3.FPGA的testbench中如何将输出数据存储在文件中4.Matlab去读取testbench存储的文件数据4.1纯数字不带编码4.2 带编码的数据&#xff0c;如定点数 微信公众号获取更多FPGA相关源码&#xff1a; 1.前言 前面一篇文章讲了&…

ESP32-C3模组上实现蓝牙BLE配网功能(2)

接前一篇文章&#xff1a;ESP32-C3模组上实现蓝牙BLE配网功能&#xff08;1&#xff09; 本文内容参考&#xff1a; 《ESP32-C3 物联网工程开发实战》 乐鑫科技 蓝牙的名字由来是怎样的&#xff1f;为什么不叫它“白牙”&#xff1f; 特此致谢&#xff01; 一、蓝牙知识基础…

[数据集][目标检测]吉他检测数据集VOC+YOLO格式66张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;66 标注数量(xml文件个数)&#xff1a;66 标注数量(txt文件个数)&#xff1a;66 标注类别数…

Three.js——tween动画、光线投射拾取、加载.obj/.mtl外部文件、使用相机控制器

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 ⚡开源项目&#xff1a; rich-vue3 &#xff08;基于 Vue3 TS Pinia Element Plus Spring全家桶 MySQL&#xff09; &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1…

数据结构基础篇(6)

二十三、队列的表示和操作的实现 相关术语 队列是仅在表尾进行插入操作&#xff0c;在表头进行删除操作的线性表表尾既a~n段&#xff0c;称对尾&#xff1b;表头a~1段&#xff0c;称队头它是一种先进先出&#xff08;FIFO&#xff09;的线性表 入队&#xff1a;插入元素出队&a…

YOLOv8 segment介绍

1.YOLOv8图像分割支持的数据格式&#xff1a; (1).用于训练YOLOv8分割模型的数据集标签格式如下: 1).每幅图像对应一个文本文件&#xff1a;数据集中的每幅图像都有一个与图像文件同名的对应文本文件&#xff0c;扩展名为".txt"; 2).文本文件中每个目标(object)占一行…

iReport的下载与安装

下载官网&#xff1a;Home - Jaspersoft Community 网盘下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1Oy5opY4GxPZ_mllTlBZ-2w 提取码&#xff1a;75do 安装就是双击后一直点击下一步 安装好了之后要配置jdk环境 iReport 目前 并不支持 高版本jdk 只支持…

TQSDRPI开发板教程:UDP收发测试

项目资源分享 链接&#xff1a;https://pan.baidu.com/s/1gWNSA9czrGwUYJXdeuOwgQ 提取码&#xff1a;tfo0 LWIP自环教程&#xff1a;https://blog.csdn.net/mcupro/article/details/139350727?spm1001.2014.3001.5501 在lwip自环的基础上修改代码实现UDP的收发测试。新建一…

嫁接打印:经济与实用的完美结合

在制造领域&#xff0c;寻求经济且好用的技术方案至关重要。而在模具制造中&#xff0c;3D 打印随形水路在提升冷却效率和产品良率方面的卓越表现已得到广泛认同。如何更经济的应用3D打印技术&#xff0c;就不得不说嫁接打印了。 在嫁接打印的制造过程中&#xff0c;产品的一部…

Codeforces Round 900 (Div. 3)

文章目录 A. How Much Does Daytona Cost?题意&#xff1a;题解&#xff1a;代码&#xff1a; B. Aleksa and Stack题意&#xff1a;题解&#xff1a;代码&#xff1a; C. Vasilije in Cacak题意&#xff1a;题解&#xff1a;代码&#xff1a; E. Iva & Pav题意&#xff1…

音视频开发—H264 SPS 和 PPS 参数说明

文章目录 序列参数集 (SPS, Sequence Parameter Set)SPS的主要内容&#xff1a; 图像参数集 (PPS, Picture Parameter Set)PPS的主要内容&#xff1a; Slice Header 结构 在H.264视频编码标准中&#xff0c;SPS和PPS是关键的参数集&#xff0c;它们提供了解码所需的各种配置信息…

Pytorch创建张量

文章目录 1.torch.from_numpy()2. torch.zeros()3. torch.ones()4. torch.arange()5. torch.linspace()6. torch.logspace()7. torch.eye()8. torch.empty()9. torch.full()10. torch.complex()10. torch.rand()10. torch.randint()11. torch.randn12. torch.normal()13. torch…