线程池和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.三步式混合编译仿真…...

【Java学习笔记】4.Java 对象和类
前言 本章介绍Java的对象和类。 Java 对象和类 Java作为一种面向对象语言。支持以下基本概念: 多态继承封装抽象类对象实例方法重载 本节我们重点研究对象和类的概念。 对象:对象是类的一个实例(对象不是找个女朋友)&#x…...

39. 实战:基于api接口实现视频解析播放(32接口,窗口化操作,可导出exe,附源码)
目录 前言 目的 思路 代码实现 需要导入的模块 1. 导入解析网站列表,实现解析过程 2. 设计UI界面 3. 设置窗口居中和循环执行 4. 注意事项 完整源码 运行效果 总结 前言 本节将类似34. 实战:基于某api实现歌曲检索与下载(附完整…...

基于灵动 MM32 微控制器的便携式血氧仪方案
基于灵动 MM32 微控制器的便携式血氧仪: - Cortex-M0() 最高主频 72MHz 可实现血氧饱和度信号采集、算法操作和 LED 显示操作 - 高性能的 1Msps 12b ADC 能对光电采样结果进行大数据量的暂存和处理,提高采样的效率并有助于对结果做高精度的计算 - 100…...

2022秋-2023-中科大-数字图像分析-期末考试试卷回忆版
今天晚上刚考完,心累,在这里继续授人以渔(仅供参考,切勿对着复习不看ppt,ppt一定要过两遍)。 注意:往年的经验贴,到此为止,全部作废,一个没考。千万不要只对着复习,SIFT没考&#x…...

【matplotlib】条形图及垂线显示小技巧 |一些有用参考帖子收集
最近在画图。一方面看论文看思路,一方面用数据跑图出论文雏形。 有些帖子写得很好,不记录的话下次还想看就只能随缘了。 帖子 博客:nxf_rabbit75 matplotlib技巧—9.共享坐标轴、创建多个subplot、调整横坐标、放置文本框、latext文字、平移…...

Go的bytes.Buffer
Go的bytes.Buffer 文章目录Go的bytes.Buffer一、bytes.Buffer 的基础知识二、bytes.Buffer类型的值,已读计数的作用三、bytes.Buffer的扩容策略四、bytes.Buffer的哪些方法会造成内容的泄露一、bytes.Buffer 的基础知识 与strings.Builder一样,bytes.Bu…...

k8s学习之路 | Day19 k8s 工作负载 Deployment(上)
文章目录1. Deployment 基础1.1 什么是 Deployment1.2 简单体验 Deployment1.3 Deployment 信息描述1.4 如何编写 Deployment2. Deployment 简单特性2.1 赋予 Pod 故障转移和自愈能力2.2 更新 Deployment2.3 回滚 Deployment2.4 暂停、恢复 Deployment 的上线过程2.5 Deploymen…...

php宝塔搭建部署实战六零导航页LyLme_Spage源码
大家好啊,我是测评君,欢迎来到web测评。 本期给大家带来一套php开发的六零导航页LyLme_Spage源码。感兴趣的朋友可以自行下载学习。 技术架构 PHP7.0 nginx mysql5.7 JS CSS HTMLcnetos7以上 宝塔面板 文字搭建教程 下载源码,宝塔添…...

SpringBoot (三) 整合数据库访问 jdbcTemplate、MyBatis
哈喽,大家好,我是有勇气的牛排(全网同名)🐮🐮🐮 有问题的小伙伴欢迎在文末评论,点赞、收藏是对我最大的支持!!!。 Spring Data了解下࿱…...

机器学习、数据挖掘和统计模式识别学习(Matlab代码实现)
目录 💥1 概述 📚2 运行结果 🎉3 参考文献 👨💻4 Matlab代码 💥1 概述 机器学习是让计算机在没有明确编程的情况下采取行动的科学。在过去的十年中,机器学习为我们提供了自动驾驶汽车&…...