线程池和ThreadLocal详解
线程池和ThreadLocal详解
- 线程池
- 池化模式:
- 线程池里的线程数量设定为多少比较合适?
- 添加线程规则:
- 实现原理:
- 线程池实现任务复用的原理
- 线程池状态:
- Executors 创线程池工具类
- 手动创建(更推荐):
- 自动创建:
- ExecutorService 执行线程池
- 运行:
- 关闭:
- 其他:
- execut() 执行流程:
- 钩子方法
- ThreadLocal
- 优点:
- 创建:
- 使用场景
- 一、每个线程需要一个独享的对象
- 二、每个线程内需要保存全局变量(例如在拦截器中获取用户信息)
- 原理
- 注意
- 内存泄漏:
- 如何避免内存泄露(阿里规约)
- 空指针异常:
线程池
java线程池是 JDK1.5提供 juc(java.util.concurrent)包中,底层实现其实就是 Callable 和 Future 接口

池化模式:
- 将大量我们需要的对象提前创建好,放在一个池(概念),对象提前创建完成,也不需要销毁对象,所以说使用效率比较好。
- 优点:使用效率较好,避免对象的 重复创建 和 销毁。
- 缺点:内存占有较高,池的数量难以把控。
线程池里的线程数量设定为多少比较合适?
- CPU 密集型(加密、计算hash等)︰最佳线程数为CPU核心数的 1-2 倍左右。
- 耗时 IO 型(读写数据库、文件、网络读写等)︰最佳线程数一般会大于 CPU 核心数很多。
- 参考 Brain Goetz 推荐的计算方法︰线程数 = CPU核心数 * (1+平均等待时间/平均工作时间)
添加线程规则:
- 最开始线程总数小于 corePoolSize 时,即使有线程处于空闲状态新任务到来时,也会创建一个新线程。
- 直到线程数等于 corePoolSize,再来任务时,会把任务放入队列去等待。
- 直到队列也满了,如果此时线程数小于 maximumPoolSize,则会再创建新线程来执行任务。
- 如果队列已满,并且线程数已经扩大到等于 maximumPoolSize时,再尝试添加任务时,会被拒绝。

- 特点:
- 若 corePoolSize == maximumPoolSize,则相当于 newFixedThreadPool();
- 线程池希望保持较少的线程数,并且只有在负载变得很大时才增加它。
- 只有在队列填满时才创建多于corePoolSize的线程,如果使用的是无界队列,那么线程数就不会超过 corePoolSize。
实现原理:
- 线程池组成部分
- 线程池管理器
- 工作线程
- 任务队列
- 任务接口(Task)
线程池实现任务复用的原理
- 相同线程执行不同任务
- 不需要重复的启动线程,循环的从队列中取出新任务并执行其 run() 方法。
线程池状态:
- RUNNING:接受新任务 并 处理排队任务。
- SHUTDOWN:不接受新任务,但处理排队任务。
- STOP:不接受新任务,也不处理排队任务,并中断正在进行的任务。
- TIDYING:所有任务都已终止,workerCount 为零时,线程会转换到 TIDYING 状态,并将运行 terminate() 钩子方法。
TERMINATED:terminate()钩子方法 运行完成。
Executors 创线程池工具类
- 根据情况,我们创建很多种不同场景的线程池 Executors.new…();
- 创建线程池返回 ExecutorService 类对象。
手动创建(更推荐):
new ThreadPoolExecutor(7个参数)
-
最原始的线程池,所有其他的线程池底层都是用的此线程池实现的。
-
前== 五个参数 ==为必须参数,面试题经常考问!!!
-
1、 corePoolSize:核心线程数,线程池中始终存活的线程数。
-
2、 maximumPoolSize:最大线程数
- 线程池中允许的最大线程数,当线程池的任务队列满了之后可以创建的最大线程数。
-
3、 keepAliveTime:最大线程数可以存活的时间
- 如果线程池当前的线程数多于 corePoolSize,那么如果多余的线程空闲时间超过 keepAliveTime,它们就会被终止。
-
4、unit:时间单位是和参数 3 存活时间配合使用的,合在一起用于设定线程的存活时间 ,参数 keepAliveTime 的时间单位有以下 7 种可选:
- TimeUnit.DAYS:天; TimeUnit.HOURS:小时; TimeUnit.MINUTES:分; TimeUnit.SECONDS:秒
- TimeUnit.MILLISECONDS:毫秒; TimeUnit.MICROSECONDS:微妙; TimeUnit.NANOSECONDS:纳秒
-
5、 workQueue:一个阻塞队列,用来存储线程池等待执行的任务,均为线程安全,它包含以下 7 种类型:
- ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。
- LinkedBlockingQueue:一个由链表结构组成的无界阻塞队列。
- SynchronousQueue:直接交接,一个不存储元素的阻塞队列,即直接提交给线程不保持它们。
- PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。
- DelayQueue:延迟队列:一个使用优先级队列实现的无界阻塞队列,只有在延迟期满时才能从中提取元素。
- LinkedTransferQueue : 一个由链表结构组成的无界阻塞队列,与 SynchronousQueue 类似,还含有非阻塞方法。
- LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。
-
6、threadFactory:线程工厂,主要用来创建线程。
- 默认为正常优先级、非守护线程(Executors.defaultThreadFactory())。
- 创建出来的线程都在同一个线程组。
- 如果自己指定 ThreadFactory,那么就可以改变线程名、线程组、优先级、是否是守护线程等。
-
7、handler:拒绝策略,拒绝处理任务时的策略,系统提供了 4 种可选
- AbortPolicy:默认策略,拒绝并直接抛出异常。
- DiscardPolicy:默默的丢弃任务,不会通知。
- DiscardOldestPolicy:丢弃队列中存在时间最久的任务。
- CallerRunsPolicy:让提交任务的线程去执行。
- 优点:不会放弃执行任务,并且能够使提交任务的速度降低下来(负反馈)
-
很多公司都 【强制使用】 这个最原始的方式创建线程池,因为这是可控的。
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(500, 2000, 1, TimeUnit.MINUTES, new ArrayBlockingQueue<Runnable>());
自动创建:
- Executors.newFixedThreadPool(int n):创建一个 可重用固定大小 的线程池,可控制并发的线程数,超出的线程会在队列中等待。
- 适用场景:并发量不会发生变化(并发量的变化非常小)
new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
- Executors.newCachedThreadPool([int n]):创建一个== 可缓存 ==的线程池。
- 若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程。
- 适用场景:并发量变化比较明显,建议使用此方法
new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
- ScheduledExecutorService Executors.newScheduledThreadPool(): 创建 延迟 后的线程池,可以设置间隔。
- 返回 ScheduledExecutorService类 对象:
- schedule(Runnable command, long delay, TimeUnit unit):指定延迟时间后创建线程。
- scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit):
- 在给定的初始延迟后首先启用,然后根据间隔时间 period 执行。
- 返回 ScheduledExecutorService类 对象:
(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new DelayedWorkQueue());
- Executors.newSingleThreadExecutor(): 单线程的线程池,只会用唯一的工作线程来执行任务。
new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));
- Executors.newWorkStealingPool():创建一个抢占式执行的线程池(任务执行顺序不确定)
- 注意此方法只有在 JDK 1.8+ 版本中才能使用。
ExecutorService 执行线程池
运行:
- execute(Runnable task):运行线程。
- submit():运行线程(更加强大),常用于创建Runnable匿名内部类

ExecutorService executor = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {final int idx = i;executor.submit(() -> System.out.println(Thread.currentThread().getName() + "————》" + idx));
}
关闭:
- shutdown():拒绝新任务,等 正在执行 以及 阻塞队列 中的任务执行完毕后就停止线程池。
- isShutdown():判断线程池是否已经开始了关闭工作,也就是是否执行了 shutdown 或者 shutdownNow 方法。。
- IsTerminated():判断线程池是否已经完全终止。(正在执行、阻塞队列都已清空)
- awaitTermination(long timeout, TimeUnit unit):检测在指定时间内(此时当前线程阻塞),线程池是否会完全终止。
- List< Runnable> shutdownNow():立刻完全停止线程池,中断正在执行的线程,并将阻塞队列里的线程返回。
- 线程 run() 方法在运行期间被中断会抛出 InterruptedException,可以使用 catch 捕获。
其他:
- BlockingQueuegetQueue():获取阻塞队列
execut() 执行流程:
public void execute(Runnable command) {if (command == null)throw new NullPointerException();int c = ctl.get();if (workerCountOf(c) < corePoolSize) {if (addWorker(command, true))return;c = ctl.get();}// 线程池正在运行,且队列没满if (isRunning(c) && workQueue.offer(command)) {int recheck = ctl.get();// 线程停止运行了,则删除任务,并拒绝if (! isRunning(recheck) && remove(command))reject(command);// 核心工作线程没有了,创建一个空的核心工作线程else if (workerCountOf(recheck) == 0)addWorker(null, false);}// 队列满了,尝试添加额外线程,此处 false 代表添加的是额外线程(<= 最大线程数)else if (!addWorker(command, false))reject(command);
}
钩子方法
-
在每个任务执行前后进行操作
- 例如:日志、统计、暂停
-
继承自线程池 ThreadPoolExecutor ,并重写构造器
-
可重写方法:
- beforeExecute():线程执行前的操作。
- afterExecute():线程执行后的操作。
示例:实现线程池的暂停、恢复操作,(省略了构造器)
public class PauseableThreadPool extends ThreadPoolExecutor {// 线程池是否处于暂停状态private boolean isPaused;private final ReentrantLock lock = new ReentrantLock();private Condition unPaused = lock.newCondition();public void pause() {lock.lock();try {isPaused = true;} finally {lock.unlock();}}@Overrideprotected void beforeExecute(Thread t, Runnable r) {super.beforeExecute(t, r);lock.lock();try {while (isPaused) {unPaused.await();}} catch (InterruptedException e) {throw new RuntimeException(e);} finally {lock.unlock();}}public void resume() {lock.lock();try {isPaused = false;unPaused.notifyAll();} finally {lock.unlock();}}
}
ThreadLocal
-
在 JDK1.2 的版本中已经为线程对象提供了副本对象,特点是:每一个线程都 独立拥有 ThreadLocal 对象。
-
作用1:在任何线程的方法中都可以轻松获取到该对象。
在 ThreadLocal 初始化时加入对象。 -
作用2:让某个需要用到的对象在线程间隔离(每个线程都有自己的独立的对象)
在线程中 set 对象。 -
ThreadLocal 不支持继承性
- 同一个 ThreadLocal 变量在父线程中被设置值后,那么在子线程中是获取不到的。
-
InheritableThreadLocal 类是可以做到支持继承性。
优点:
- 达到线程安全。
- 不需要加锁,提高执行效率。
- 更高效地利用内存、节省开销。
- 相比于场景一每个任务都新建一个 SimpleDateFormat,显然用 ThreadLocal 可以节省内存和开销
- 免去传参的繁琐
- 不需要每次都传同样的参数
- ThreadLocal使得代码耦合度更低,更优雅
创建:
- 一定要将 ThreadLocal 定义成 静态变量 static
- ThreadLocal 需要一个泛型;
public static ThreadLocal<String> threadLocal1 = new Thread<>();
public static ThreadLocal<SimpleDateFormat> threadLocal2 = ThreadLocal.withInitial(() ->new SimpleDateFormat("yyyy-MM-dd hh:mm:ss")
); // 初始化并设置值
-
get():获取 ThreadLocal 中当前线程共享的变量的值。
- 在 线程开启(或调用 remove())后第一次调用 get() 的时候,会调用 initialValue() 来得到值。
-
remove():移除 ThreadLocal中当前线程共享的变量的值。
- 最后 一定 要移除值!!!,否则很容易出现 内存溢出。
-
initialValue():该方法会返回当前线程对应的 “初始值”,这是一个延迟加载的方法。默认为 null。
- ThreadLocal.withInitial():初始化 ThreadLocal 并设置 initialValue()。
-
set():设置 ThreadLocal 中当前线程共享的变量的值。
-
set 和 setInitialValue 结果都是调用 map.set,只不过是起点和入口不一样。
public static ThreadLocal<SimpleDateFormat> threadLocal2 = ThreadLocal.withInitial(() ->new SimpleDateFormat("yyyy-MM-dd hh:mm:ss")
);
@Test
void contextLoads() {ExecutorService executorService = Executors.newFixedThreadPool(10);for (int i = 0; i < 1000; ++i) {final int finalI = i;executorService.submit(() ->System.out.println(threadLocal2.get().format(new Date(1000 * finalI))));}executorService.shutdownNow();
}
使用场景
一、每个线程需要一个独享的对象
- 通常是工具类,典型需要使用的类有 SimpleDateFormat 和 Random
- 比喻∶教材只有一本,一起做笔记有线程安全问题。复印后就没问题。
- 示例:
- 线程池有1000个线程,每个线程都要使用 SimpleDateFormat 工具类,若直接使用,则会创建 1000 个工具类,开销巨大。
- 这时可以创建一个 static 的 SimpleDateFormat 对象,让线程池的每个核心线程都使用这个静态对象,就防止了重复的创建。
- 但是,由于 SimpleDateFormat 类是线程不安全的,会出现重复的计算结果。
- 此时,可能会想到通过加锁,确实能使结果正确,但这样同一时间就只有一个线程运行了,那何必高并发呢?
- 这时,就可以使用 ThreadLocal 了,让每个核心线程内部都有自己独有的 SimpleDateFormat 对象,如图:

public static ThreadLocal<SimpleDateFormat> threadLocal2 = ThreadLocal.withInitial(() ->new SimpleDateFormat("yyyy-MM-dd hh:mm:ss")
);
@Test
void contextLoads() {ExecutorService executorService = Executors.newFixedThreadPool(10);for (int i = 0; i < 1000; ++i) {final int finalI = i;executorService.submit(() ->System.out.println(threadLocal2.get().format(new Date(1000 * finalI))));}executorService.shutdownNow();
}
二、每个线程内需要保存全局变量(例如在拦截器中获取用户信息)
- 可以让不同方法直接使用,避免参数传递的麻烦
原理
- ThreadLocal 底层是将 值 存放在线程的 ThreadLocalMap 中的。
- 通过 getMap(Thread.currentThread()) 获取 ThreadLocalMap 对象。

注意
内存泄漏:
-
内存泄露为 程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光,广义并通俗的说,就是:不再会被使用的对象或者变量占用的内存 不能被回收,就是内存泄露。
-
由于 ThreadLocalMap 继承了 WeakReference 类,属于弱引用对象。
- 弱引用,JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。在java中,用java.lang.ref.WeakReference 类来表示。可以在缓存中使用弱引用。
-
ThreadLocalMap 的每个 Entry 都是一个对 key 的弱引用,同时是一个对 value 的强引用。
-
正常情况下,当线程终止,保存在 ThreadLocal 里的 value 会被垃圾回收,因为没有任何强引用了但是,如果线程不终止(比如线程需要保持很久),那么 key 对应的 value 就不能被回收,因为有以下的 调用链:
- Thread —> ThreadLocalMap —>Entry ( key为null) —> Value
-
因为 value 和 Thread 之间还存在这个强引用链路,所以导致 value 无法回收,就可能会出现 OOM
- ThreadLocalMap 也有相应的处理方法(在 resize() 中):扫描 key 为 null 的Entry,并把对应的 value 设置为 null
- 但前提是得调用 map.set() 或 remove() 。
- ThreadLocalMap 也有相应的处理方法(在 resize() 中):扫描 key 为 null 的Entry,并把对应的 value 设置为 null
-
如果一个ThreadLocal 不被使用,就可能导致value的内存泄漏
如何避免内存泄露(阿里规约)
- 调用 remove 方法,就会删除对应的 Entry 对象,可以避免内存泄漏,所以使用完 ThreadLocal 之后,应该调用 remove方法。
空指针异常:
- 在创建 ThreadLcal时泛型是 包装类,如 Long,而若获取 value 时,接收返回值的对象只是基本数据类型时,有可能会拆箱错误。
- 比如,在 ThreadLocal 没有设置值时,默认是 null,而将 null 赋给 long,就会 空指针异常。
- 若使用的是包装类 Long,则不会空指针异常。
- 比如,在 ThreadLocal 没有设置值时,默认是 null,而将 null 赋给 long,就会 空指针异常。
- 因此,在接收 get() 返回值时,不建议使用 基本数据类型。
相关文章:
线程池和ThreadLocal详解
线程池和ThreadLocal详解线程池池化模式:线程池里的线程数量设定为多少比较合适?添加线程规则:实现原理:线程池实现任务复用的原理线程池状态:Executors 创线程池工具类手动创建(更推荐):自动创…...
[深入理解SSD系列综述 1.7] SSD固态存储市场发展分析与预测_固态存储技术发展方向(2022to2023)
前言 自2020年疫情爆发以来,远程办公、网上教育、流媒体等等应用引爆对消费电子及云服务的需求增长,全球数字化转型加速,带来了两年的闪存风光时刻。然而,进入2022年,在俄乌冲突、疫情重燃、通胀上升等一系列事件冲击下,全球经济下行风险加剧,对智能手机、PC等科技产品的…...
【2021.12.25】ctf逆向中常见加密算法和编码识别
【2021.12.25】ctf逆向中常见加密算法和编码识别(含exe及wp) 文章目录【2021.12.25】ctf逆向中常见加密算法和编码识别(含exe及wp)0、前言1、基础加密手法2、base64(1)原理:(2&#…...
【数据结构初阶】堆排序
目录 前言 概念 堆排序的实现 1.建堆 (1)堆向上调整算法 (2)堆的向下调整算法 2. 利用堆删除思想来进行排序 3.堆排序的时间复杂度 4.源码 总结 前言 前边我们学习了堆的实现,对堆的每个接口都进行了详细的讲…...
Day5: platformDriver-1
Platform Driver (1) Linux kernel中大部分设备可以归结为平台设备,因此大部分的驱动是平台驱动(patform driver) 什么是平台设备 平台设备是linux的设备模型中一类设备的抽象。 内核中的描述: Platform devices are devices t…...
开发手册——一、编程规约_7.控制语句
这篇文章主要梳理了在java的实际开发过程中的编程规范问题。本篇文章主要借鉴于《阿里巴巴java开发手册终极版》 下面我们一起来看一下吧。 1. 【强制】在一个 switch 块内,每个 case 要么通过 break / return 等来终止,要么注释说明程序将继续执行到哪…...
python每日学9 : windows上配置gitee的远程仓库,git的初步使用
在开发中,如果遇到复杂的项目,使用版本控制是非常有必要的,如果涉及到多端开发,那么还需要使用远程仓库。本文作个简单记录,记录下git初步使用。 1 下载与安装 git还有几个ui版本,但是开始使用的话&#…...
精确率与召回率,ROC曲线与PR曲线
精确率与召回率,ROC曲线与PR曲线 在机器学习的算法评估中,尤其是分类算法评估中,我们经常听到精确率(precision)与召回率(recall),ROC曲线与PR曲线这些概念,那这些概念到底有什么用处呢? 首先,…...
现代操作系统——Linux架构与学习
小白的疑惑 在我决定从事嵌入式(应用层)方面的工作时,我查询了大量资料该如何学习,几乎所有观点不约而同的都指向了学习好Linux,大部分工作都是在Linux环境下来进行工作的。于是我雄心勃勃的去下载Linux,可…...
中文代码82
PK 嘚釦 docProps/PK 嘚釦羸 r docProps/app.xml潙蚽?勶曻Q顗濔S? 錞礖剅D柍珘m?鳞?ぷ辷f硌?2?upc厭Y樐8 rU y搪m眾&a?珪?紓 玺鶋瑣襚? ?i嘲rN?布倖儇?攊橌??嚗猝)芻矂2吟腊K湞?CK臶>鸘\?ΔF滋齢q旮T?桀?;偉 A軥v蕯朾偤佷3?е…...
顺序表(一篇带你掌握顺序表)
目录 一、顺序表是什么 1.1 概念 1.2 分类 1.3 结构 二、顺序表的基本操作 2.1 前绪准备 2.2 初始化 2.3 扩容 2.5 尾插 2.6 打印 2.7 尾删 2.8 头插 2.9 头删 2.10 在pos位置插入 2.11 删除pos位置的数据 2.12 查找 三、完整代码 3.1 Test.c文件 3.2 SeqList.h…...
【SpringCloud】SpringCloud教程之Feign实战
目录前言SpringCloud Feign远程服务调用一.需求二.两个服务的yml配置和访问路径三.使用RestTemplate远程调用(order服务内编写)四.构建Feign(order服务内配置)五.自定义Feign配置(order服务内配置)六.Feign配置日志(oder服务内配置)七.Feign调优(order服务内配置)八.抽离Feign前…...
嵌入式linux必备内存泄露检测神器
Valgrind介绍 Valgrind是一个可移植的动态二进制分析工具集,主要用于发现程序中的内存泄漏、不合法内存访问、使用未初始化的内存、不正确的内存释放以及性能问题等,可在Linux和Mac OS X等平台上使用。 Valgrind由多个工具组成,其中最常用的…...
设计模式之行为型模式
四、行为型模式 行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。 行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在…...
解密 三岁的三岁到底为什么叫做三岁?
机缘 那一年,一次奇奇怪怪的挫折与一次奇奇怪怪的成长。 在学习Python的路上总觉得少了点什么,是心情?是机遇?还是力量? 都不是又都是! 缺少一个实践和记忆的平台 记性不好是硬伤 前一天学的下一秒就忘记了…...
id选择器
id选择器可以为特定的id的标签进行css美化 使用方法: 标签内设好 id值, CSS的id选择器以“#id名”来调用 注意 所有标签都有id值id属性值类似于身份证号码,在一个页面中是唯一的值,不可重复一个标签上只能有一个id属性值一个id属性…...
《科技之巅3》读书笔记
文章目录书籍信息人工智能,“吃一堑长一智”的机器人机交互,为解决“交流障碍”问题而生硬件与算法,好马还需好鞍模式创新,赋予技术新的定义云与数据共享,灵活应对信息的爆发式增长“机器人”,从电影和小说…...
18.用于大型程序的工具
文章目录用于大型程序的工具18.1异常处理18.1.1抛出异常栈展开栈展开过程中对象被自动销毁析构函数与异常异常对象18.1.2捕获异常查找匹配的处理代码重新抛出捕获所有异常的处理代码18.1.3函数try语句块与构造函数18.1.4noexcept异常说明违反异常说明异常说明的实参noexcept运算…...
mysql一主键uuid和自增的选择
文章目录 1.自增ID的优缺点1.1 优点1.2 缺点1.3 不适合以自增ID主键作为主键的情况2.UUID作为主键2.1 介绍2.2 优点2.3 缺点3.有序UUID作为主键3.1 介绍3.2 演示使用3.2.1 前提知识3.2.1.1 数据类型 - binary3.2.1.2 函数 - hex()3.2.1.3 函数 - unhex()3.2.2 数据库层3.2.3 JA…...
【EDA工具使用】——VCS和Verdi的联合仿真的简单使用
目录 1.芯片开发所需的工具环境 2.编译仿真工具 3.三步式混合编译仿真(最常用)编辑 4.两步式混合编译仿真编辑 5.VCS的使用 6.verdi的使用 1.产生fsdb文件的两种方法编辑 1.芯片开发所需的工具环境 2.编译仿真工具 3.三步式混合编译仿真…...
DAMOYOLO-S基础教程:理解count字段与实际业务中目标计数逻辑映射
DAMOYOLO-S基础教程:理解count字段与实际业务中目标计数逻辑映射 1. 从一次“数数”的困惑说起 前两天,一个做零售分析的朋友找我帮忙。他兴奋地告诉我,他们用上了最新的AI目标检测模型,想自动统计货架上的商品数量。他上传了一…...
Qwen2.5-Coder-1.5B应用案例:快速生成网页爬虫代码实战
Qwen2.5-Coder-1.5B应用案例:快速生成网页爬虫代码实战 1. 引言:为什么选择Qwen2.5-Coder生成爬虫代码 在日常开发工作中,网页爬虫是数据采集和分析的重要工具。传统编写爬虫代码需要开发者熟悉HTTP请求、HTML解析、反爬机制处理等多个技术…...
7个步骤打造高效文件上传系统:Plupload零基础入门指南
7个步骤打造高效文件上传系统:Plupload零基础入门指南 【免费下载链接】plupload Plupload is JavaScript API for building file uploaders. It supports multiple file selection, file filtering, chunked upload, client side image downsizing and when necess…...
保姆级避坑指南:在Ubuntu 20.04上搞定Carla 0.9.15与ROS Noetic的联合仿真环境
保姆级避坑指南:Ubuntu 20.04下Carla 0.9.15与ROS Noetic联合仿真环境搭建全攻略 搭建自动驾驶仿真环境就像在雷区跳舞——稍有不慎就会触发依赖冲突、版本不兼容或环境变量错误。本文将带你用最短时间穿越这片雷区,特别针对那些官方文档没写、论坛讨论含…...
ChatGLM-6B角色扮演功能开发:基于Prompt的智能对话系统
ChatGLM-6B角色扮演功能开发:基于Prompt的智能对话系统 1. 引言 想象一下,你正在开发一个智能客服系统,需要让AI能够扮演不同角色的专业人士来回答用户问题。或者你正在创建一个教育应用,希望AI能够化身历史人物、科学导师或文学…...
SenseVoiceSmall实战案例:如何用AI分析会议录音中的情绪变化
SenseVoiceSmall实战案例:如何用AI分析会议录音中的情绪变化 1. 会议录音分析的痛点与解决方案 在日常工作中,会议录音分析一直是个耗时费力的任务。传统方法需要人工反复听取录音,不仅效率低下,还容易遗漏关键信息。特别是会议…...
DM数据库迁移实战:dimp与dexp版本兼容性问题解析与解决方案
1. 当DM数据库迁移遇上版本兼容性问题 最近在帮客户做DM数据库迁移时,遇到了一个典型问题:用高版本dexp导出的数据文件,无法用低版本dimp导入。这就像用最新版Word写的文档,用老版本打不开一样让人头疼。具体表现是执行导入命令时…...
GLM-4V-9B GPU高效利用:通过dtype对齐+4-bit量化实现A10G 24GB满载运行
GLM-4V-9B GPU高效利用:通过dtype对齐4-bit量化实现A10G 24GB满载运行 1. 引言 最近在折腾多模态大模型本地部署的朋友,可能都遇到过类似的问题:模型参数动辄几十上百亿,显存要求高得吓人,好不容易找到个能在消费级显…...
IAR平台华大HC32F460工程搭建避坑指南:从零到调试成功的全流程解析
1. 从KEIL到IAR的转型背景 最近两年芯片市场的价格波动,让很多工程师不得不重新评估开发工具链的选择。我作为一个用了五年KEIL的老用户,最近也被迫开始学习IAR平台。原因很简单——当ST单片机价格涨到华大HC32F460的十倍时,任何成本敏感的项…...
如何用TinyTroupe多智能体模拟优化大豆深加工工艺:提升效率的完整指南
如何用TinyTroupe多智能体模拟优化大豆深加工工艺:提升效率的完整指南 【免费下载链接】TinyTroupe LLM-powered multiagent persona simulation for imagination enhancement and business insights. 项目地址: https://gitcode.com/GitHub_Trending/ti/TinyTrou…...
