Spring Boot 线程池自定义拒绝策略:解决任务堆积与丢失问题
如何通过自定义线程池提升系统稳定性
背景
在高并发系统中,线程池管理至关重要。默认线程池可能导致:
- 资源浪费(创建过多线程导致 OOM)
- 任务堆积(队列满后任务被拒绝)
- 任务丢失(默认拒绝策略丢弃任务
为了防止这些问题,我们使用 Spring Boot 自定义线程池,并优化 异常处理 和 拒绝策略。
线程池方案设计
在 ExecutorConfig 类中,我们定义了两个线程池:
- myExecutor:用于普通任务,采用CallerRunsPolicy 避免任务丢失。
- oneExecutor:用于信号计算任务(单线程模式),具有 自定义异常处理 和 阻塞式拒绝策略。
代码解析
线程池 myExecutor(通用任务池)
@Bean(name = "myExecutor")
public Executor myExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(threadProperties.getCorePoolSize());executor.setMaxPoolSize(threadProperties.getMaxPoolSize());executor.setQueueCapacity(threadProperties.getQueueCapacity());executor.setThreadNamePrefix("signal-executor-");executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();return executor;
}
设计要点:
CallerRunsPolicy:线程池满了,主线程执行任务,防止丢失但可能影响性能。
线程池 oneExecutor(单线程计算池)
@Bean(name = "oneExecutor")
public Executor oneExecutor() {ThreadFactory threadFactory = new BasicThreadFactory.Builder().uncaughtExceptionHandler(new MyThreadException()).namingPattern("one-thread-%s").build();ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(1);executor.setMaxPoolSize(1);executor.setQueueCapacity(1);executor.setThreadFactory(threadFactory);executor.setThreadGroup(new ThreadGroup("1"));executor.setRejectedExecutionHandler(new CustomRejectedExecutionHandler());executor.initialize();return executor;
}
设计要点:
单线程池(保证任务顺序执行),如果无须,那就按照当前的服务节点配置来设置参数
自定义异常处理(防止线程因异常崩溃)
自定义拒绝策略(任务队列满时阻塞等待)
自定义异常处理
class MyThreadException implements Thread.UncaughtExceptionHandler {@Overridepublic void uncaughtException(Thread t, Throwable e) {log.error("异常: {},线程: {}", ExceptionUtils.getStackTrace(e), t.getName());}
}
作用:防止线程因未捕获异常直接终止,提升系统稳定性。当然这个是处理线程池中子任务处理业务逻辑的时候发生业务异常的处理方式,除此之外还有其他的解决方案
异常处理
- afterExecute() 处理异常(可扩展) :用于处理执行过程中抛出的异常
- uncaughtExceptionHandler 处理未捕获异常(默认 JVM 打印堆栈): 用于处理线程未捕获的异常;
- RejectedExecutionHandler 处理任务拒绝:处理任务被拒绝的情况。
处理顺序:
- 当任务执行时,如果任务抛出异常,它会首先被 afterExecute() 捕获,并且你可以在这里进行进一步的处理。
- 如果任务中的异常没有被 afterExecute() 捕获或处理,且是未捕获异常,它会交由 uncaughtExceptionHandler 进行处理。
- RejectedExecutionHandler 是处理线程池拒绝接受新任务的情况,这通常和任务执行过程中的异常无关,主要处理线程池饱和时的情况。
注意:beforeExecute() 在任务开始执行前调用,通常用于准备工作;
异常处理上,beforeExecute() 不会直接处理任务执行过程中的异常,但可以捕获并处理自己内部的异常;
相关源码分析:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
// 1️⃣ 线程池当前线程数 < corePoolSize,则尝试新增核心线程执行任务
if (workerCountOf© < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
// 2️⃣ 线程池已满,尝试加入工作队列
if (isRunning© && workQueue.offer(command)) {
int recheck = ctl.get();
if (!isRunning(recheck) && remove(command))
reject(command); // 任务队列中的任务被拒绝
else if (workerCountOf(recheck) == 0)
addWorker(null, false); // 防止线程池为空,确保有线程执行任务
}
// 3️⃣ 线程池满且队列满,尝试新增非核心线程
else if (!addWorker(command, false))
reject(command); // 线程池已满,拒绝任务
}
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // 允许中断
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
w.lock();
// 1️⃣ 执行任务
beforeExecute(wt, task);
Throwable thrown = null;
try {
task.run(); // ⚠ 任务执行点
} catch (RuntimeException x) {
thrown = x;
throw x;
} catch (Error x) {
thrown = x;
throw x;
} catch (Throwable x) {
thrown = x;
throw new Error(x);
} finally {
afterExecute(task, thrown); // 2️⃣ 任务执行后的扩展方法
}
task = null;
w.completedTasks++;
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly); // 3️⃣ 任务异常退出,删除该线程
}
}
自定义拒绝策略-重新放回队列中
public class CustomRejectedExecutionHandler implements RejectedExecutionHandler {@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {try {if (!executor.isShutdown()) {log.info("队列已满,阻塞等待...");executor.getQueue().put(r);log.info("任务已加入队列");}} catch (Exception e) {log.error("拒绝策略异常", e);}}
}
作用:
默认拒绝策略丢弃任务,而此策略会阻塞等待,确保任务不丢失。
适用于任务量较大,但不能丢失任务的场景(如消息队列处理)
自定义拒绝策略-主线程执行
/*** 自定义线程池,防止使用默认线程池导致内存溢出** @param* @return* @author bu.junjie* @date 2021/11/10 10:00*/@Bean(name = "myExecutor")public Executor myExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(threadProperties.getCorePoolSize());executor.setMaxPoolSize(threadProperties.getMaxPoolSize());executor.setQueueCapacity(threadProperties.getQueueCapacity());executor.setThreadNamePrefix("signal-executor-");// 使用此策略,如果添加到线程池失败,那么主线程会自己去执行该任务,不会等待线程池中的线程去执行,阻塞主线程executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();return executor;}
适用场景
✅ 高并发请求(如 HTTP 任务)
✅ 后台数据处理(如日志分析、批量计算)
✅ 长时间任务(如大文件处理、消息队列消费)
总结
- 自定义线程池 防止资源浪费,提升吞吐量。
- 异常处理 避免线程因未捕获异常而终止。
- 优化拒绝策略 防止任务丢失,提高系统可靠性。
线程池优化是高并发系统的关键,希望本篇博客能帮助你更好地理解和应用线程池! 🚀🚀🚀
完整代码示例
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import javax.annotation.Resource;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;/*** 线程池配置参数** @version 1.0.0* @createTime 2025-11-09 14:01*/
@Configuration
@EnableAsync
@Slf4j
public class ExecutorConfig {@Resourceprivate ThreadProperties threadProperties;/*** 自定义线程池,防止使用默认线程池导致内存溢出** @param* @return* @author bu.junjie* @date 2021/11/10 10:00*/@Bean(name = "myExecutor")public Executor myExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(threadProperties.getCorePoolSize());executor.setMaxPoolSize(threadProperties.getMaxPoolSize());executor.setQueueCapacity(threadProperties.getQueueCapacity());executor.setThreadNamePrefix("signal-executor-");// 使用此策略,如果添加到线程池失败,那么主线程会自己去执行该任务,不会等待线程池中的线程去执行,阻塞主线程executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();return executor;}/*** 信号计算时的线程池(1号线程池)** @param* @return* @author bu.junjie* @date 2022/1/5 13:01*/@Bean(name = "oneExecutor")public Executor oneExecutor() {ThreadFactory threadFactory = new BasicThreadFactory.Builder().uncaughtExceptionHandler(new MyThreadException()).namingPattern("one-thread-%s").build();ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(1);executor.setMaxPoolSize(1);executor.setThreadFactory(threadFactory);executor.setQueueCapacity(1);executor.setThreadGroup(new ThreadGroup("1"));executor.setRejectedExecutionHandler(new CustomRejectedExecutionHandler());executor.initialize();return executor;}class MyThreadException implements Thread.UncaughtExceptionHandler {/*** Method invoked when the given thread terminates due to the* given uncaught exception.* <p>Any exception thrown by this method will be ignored by the* Java Virtual Machine.** @param t the thread* @param e the exception*/@Overridepublic void uncaughtException(Thread t, Throwable e) {log.error("MyThreadException is exception=【{}】,Thread = 【{}】", ExceptionUtils.getStackTrace(e), t.getName());}}/*** 拒绝策略优化** @param* @author bu.junjie* @date 2022/1/8 14:06* @return*/public class CustomRejectedExecutionHandler implements RejectedExecutionHandler {@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {try {// 核心改造点,由blockingqueue的offer改成put阻塞方法if (!executor.isShutdown()) {long start = System.currentTimeMillis();log.info("当前阻塞队列已满开始请求存放队列束!!!");executor.getQueue().put(r);log.info("存放阻塞队列成功,阻塞时间time = 【{}】", System.currentTimeMillis() - start);}} catch (Exception e) {e.printStackTrace();}}}}
思考
为什么拒绝策略要重新抛出异常?
我们会发现默认的四种拒绝策略在处理完业务逻辑之后还会重新抛出异常,就算你是自定义的拒绝策略也需要重新抛出异常,为什么呢?不抛出会怎么样?
如果不抛出异常,调用方(业务代码)无法感知任务被拒绝,可能导致任务丢失或业务逻辑异常。
场景分析
当线程池队列满了时,会触发 rejectedExecution 方法。如果我们只是记录日志,而不抛出异常:
- 主线程会继续执行,但任务并未真正执行,业务方无法感知到这个问题。
- 可能导致数据丢失,尤其是在关键业务(如支付、订单、消息处理)场景中。
重新抛出异常的好处
✅ 保证调用方可以感知任务拒绝,决定是否降级处理、重试或报警。
✅ 防止静默丢失任务,保证业务的可靠性。
✅ 与 Spring 线程池默认行为保持一致,防止意外吞掉异常。
代码示例
❌ 错误示例(未抛出异常,可能导致任务丢失)
public class CustomRejectedExecutionHandler implements RejectedExecutionHandler {@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {try {if (!executor.isShutdown()) {log.warn("队列已满,任务阻塞等待...");executor.getQueue().put(r); // 可能抛出异常log.info("任务已放入队列");}} catch (InterruptedException e) {Thread.currentThread().interrupt(); // 仅恢复中断状态,但未通知调用方}}
}
问题:
调用方不会收到异常,以为任务已经成功执行,但其实可能丢失了。
例如,在支付系统中,如果订单更新任务丢失,可能导致订单状态未更新。
✅ 正确示例(重新抛出异常,保证调用方可感知)
public class CustomRejectedExecutionHandler implements RejectedExecutionHandler {@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {try {if (!executor.isShutdown()) {log.warn("队列已满,阻塞等待...");executor.getQueue().put(r);log.info("任务成功进入队列");return; // 任务成功加入队列后不需要抛异常}} catch (InterruptedException e) {Thread.currentThread().interrupt(); // 恢复线程中断状态throw new RejectedExecutionException("任务提交被中断", e);} catch (Exception e) {log.error("任务拒绝策略发生异常", e);throw new RejectedExecutionException("自定义拒绝策略异常", e);}}
}
改进点:
任务成功放入队列时不会抛异常,避免不必要的错误。
如果 put() 失败,抛出 RejectedExecutionException,让业务方感知。
捕获 InterruptedException 并恢复中断状态,避免影响后续任务。
其实这个原因和为什么需要恢复线程中断一样的逻辑,也是为了让调用方感知到
业务方如何处理异常?
如果 rejectedExecution 抛出 RejectedExecutionException,业务代码可以捕获异常并进行降级,例如:
try {executor.execute(task);
} catch (RejectedExecutionException e) {log.error("线程池已满,任务执行失败,进行降级处理", e);// 业务降级策略,例如:saveToDatabaseForLaterProcessing(task);
}
降级方案:如果线程池拒绝任务,可以存入 数据库、MQ 或 重试队列,避免任务丢失。
结论
🚀 必须重新抛出异常,否则:
- 任务可能悄悄丢失,业务方无法感知。
- 可能影响数据一致性(如支付、订单、日志处理)。
- 业务代码无法主动补救(重试、降级等)。
最佳实践:
- 成功放入队列 → 不抛异常
- 任务无法处理 → 抛出 RejectedExecutionException,让调用方感知
这样可以既保证任务不丢失,又确保调用方有能力处理拒绝任务!🔥
自定义拒绝策略put()方法?
其实默认拒绝策略是offer()方法是非阻塞的,也就是只要队列中的任务只要有,那就去创建子线程,直至触发拒绝策略
✅ 正确示例
public class CustomRejectedExecutionHandler implements RejectedExecutionHandler {@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {try {System.out.println("队列已满,阻塞等待...");executor.getQueue().put(r); // 阻塞等待队列有空位System.out.println("任务重新加入队列:" + r.toString());} catch (InterruptedException e) {Thread.currentThread().interrupt();throw new RejectedExecutionException("任务提交失败,线程被中断", e);}}
}
相关文章:
Spring Boot 线程池自定义拒绝策略:解决任务堆积与丢失问题
如何通过自定义线程池提升系统稳定性 背景 在高并发系统中,线程池管理至关重要。默认线程池可能导致: 资源浪费(创建过多线程导致 OOM)任务堆积(队列满后任务被拒绝)任务丢失(默认拒绝策略丢…...
人工智能D* Lite 算法-动态障碍物处理、多步预测和启发式函数优化
在智能驾驶领域,D* Lite 算法是一种高效的动态路径规划算法,适用于处理环境变化时的路径重规划问题。以下将为你展示 D* Lite 算法的高级用法,包含动态障碍物处理、多步预测和启发式函数优化等方面的代码实现。 代码实现 import heapq impo…...
C#常用集合优缺点对比
先上结论: 在C#中,链表、一维数组、字典、List<T>和ArrayList是常见的数据集合类型,它们各有优缺点,适用于不同的场景。以下是它们的比较: 1. 一维数组 (T[]) 优点: 性能高:数组在内存中…...
多线程下jdk1.7的头插法导致的死循环问题
20250208 多线程下jdk1.7的头插法导致的死循环问题 多线程下jdk1.7的头插法导致的死循环问题 【新版Java面试专题视频教程,java八股文面试全套真题深度详解(含大厂高频面试真题)】 jdk1.7在hashmap扩容时使用的是头插法,所以扩容…...
MySQL的深度分页如何优化?
大家好,我是锋哥。今天分享关于【MySQL的深度分页如何优化?】面试题。希望对大家有帮助; MySQL的深度分页如何优化? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 MySQL在处理深度分页(即查询页数较大时,通常是查询…...
uniapp中使用uCharts折线图X轴数据间隔显示
1、先看官网 https://www.ucharts.cn/ 2、设置代码 "xAxisDemo3":function(val, index, opts){if(index % 2 0){return val}else {return }}, 再在数据中引入设置好样式...
VMware下Linux和macOS安装VSCode一些总结
本文介绍VMware下Linux和macOS安装VSCode的一些内容,包括VSCode编译器显示中文以及安装.NET环境和Python环境。 VSCode下载地址:Download Visual Studio Code - Mac, Linux, Windows 一.Linux系统下 1.安装中文包 按 Ctrl Shift P 打开命令面板。输…...
公司配置内网穿透方法笔记
一、目的 公司内部有局域网,局域网上有ftp服务器,有windows桌面服务器; 在内网环境下,是可以访问ftp服务器以及用远程桌面登录windows桌面服务器的; 现在想居家办公时,也能访问到公司内网的ftp服务器和win…...
【Windows/C++/yolo开发部署02:正确方法】将自定义实例分割模型导出为 ONNX 格式
【完整项目下载地址】: 【TensorRT部署YOLO项目:实例分割+目标检测】+【C++和python两种方式】+【支持linux和windows】资源-CSDN文库 目录 写在前面 环境准备 安装必要的库 下载模型并开始转换 解决依赖问题 安装 ONNX 降级 Protobuf 最终转换 总结 写在前面 在…...
国产编辑器EverEdit - 编辑辅助功能介绍
1 编辑辅助功能 1.1 各编辑辅助选项说明 1.1.1 行号 打开该选项时,在编辑器主窗口左侧显示行号,如下图所示: 1.1.2 文档地图 打开该选项时,在编辑器主窗口右侧靠近垂直滚动条的地方显示代码的缩略图,如下图所示&…...
Jupyter Notebook自动保存失败等问题的解决
一、未生成配置文件 需要在命令行中,执行下面的命令自动生成配置文件 jupyter notebook --generate-config 执行后会在 C:\Users\用户名\.jupyter目录中生成文件 jupyter_notebook_config.py 二、在网页端打开Jupyter Notebook后文件保存失败;运行代码…...
Shapefile格式文件解析和显示
Java实现GIS SHP文件格式的解析和显示,JDK19下编译,awt图形系统显示。 SHP文件对应的属性存储在DBF格式数据库中,解析见:DBASE DBF数据库文件解析_数据库文件在线解析-CSDN博客 解析SHP文件代码: public static Shap…...
大语言模型需要的可观测性数据的关联方式
可观测性数据的关联方式及其优缺点 随着现代分布式架构和微服务的普及,可观测性(Observability)已经成为确保系统健康、排查故障、优化性能的重要组成部分。有效的可观测性数据关联方式不仅能够帮助我们实时监控系统的运行状态,还…...
wordpressAI工具,已接入Deepseek 支持自动生成文章、生成图片、生成长尾关键词、前端AI窗口互动、批量采集等
基于关键词或现有内容生成SEO优化的文章,支持多种AI服务(如OpenAI、百度文心一言、智谱AI等),并提供定时任务、内容采集、关键词生成等功能。 核心功能 文章生成 关键词生成:根据输入的关键词生成高质量文章。 内容…...
解构赋值在 TypeScript 中的妙用:以 Babylon.js 的 loadModel 函数为例
在现代 JavaScript 和 TypeScript 开发中,解构赋值(Destructuring Assignment)是一种非常实用的特性,它能够让代码更加简洁、易读且高效。今天,我们就通过一个实际的例子——在 Babylon.js 中加载 3D 模型的 loadMod…...
mysql8安装时提示-缺少Microsoft Visual C++ 2019 x64 redistributable
MySQL8.0安装包mysql-8.0.1-winx64进行安装,提示:This application requires Visual Studio 2019 x64Redistributable, Please install the Redistributable then runthis installer again。出现这个错误是因为我们电脑缺少Microsoft Visual C 这个程序&…...
物品匹配问题-25寒假牛客C
登录—专业IT笔试面试备考平台_牛客网 这道题看似是在考察位运算,实则考察的是n个物品,每个物品有ai个,最多能够得到多少个物品的配对.观察题目可以得到,只有100,111,010,001(第一位是ci,第二位是ai,第三位是bi)需要进行操作,其它都是已经满足条件的对,可以假设对其中两个不同…...
学习数据结构(6)单链表OJ上
1.移除链表元素 解法一:(我的做法)在遍历的同时移除,代码写法比较复杂 解法二:创建新的链表,遍历原链表,将非val的节点尾插到新链表,注意,如果原链表结尾是val节点需要将…...
03/29 使用 海康SDK 对接时使用的 MysqlUtils
前言 最近朋友的需求, 是需要使用 海康sdk 连接海康设备, 进行数据的获取, 比如 进出车辆, 进出人员 这一部分是 资源比较贫瘠时的一个 Mysql 工具类 测试用例 public class MysqlUtils {public static String MYSQL_HOST "192.168.31.9";public static int MY…...
全志A133 android10 thermal温控策略配置调试
一,功能介绍 Thermal简称热控制系统,其功能是通过temperature sensor(温度传感器)测量当前CPU、GPU等设备的温度值,然后根据此温度值,影响CPU、GPU等设备的调频策略,对CPU、GPU等设备的最大频率…...
知识图谱智能应用系统:数据存储架构与流程解析
在当今数字化时代,知识图谱作为一种强大的知识表示和管理工具,正逐渐成为企业、科研机构以及各类智能应用的核心技术。知识图谱通过将数据转化为结构化的知识网络,不仅能够高效地存储和管理海量信息,还能通过复杂的查询和推理,为用户提供深度的知识洞察。然而,构建一个高…...
mac下生成.icns图标
笔记原因: 今日需要在mac下开发涉及图标文件的使用及icons文件的生成,所以记录一下。 网络上都是一堆命令行需要打印太麻烦了,写一个一键脚本。 步骤一 将需要生成的png格式文件重命名为“pic.png” mv xxxx.png pic.png 步骤二 下载我…...
Dev-cpp C语言编写和调用dll
Dev-cpp新建DLL项目。 dllmain.cpp #include "dll.h" #include <windows.h>int add(int a, int b) { return a b; } dll.h #ifndef _DLL_H_ #define _DLL_H_extern "C" __declspec(dllexport) int add(int a, int b);#endif 调用DLL&#…...
IDEA编写SpringBoot项目时使用Lombok报错“找不到符号”的原因和解决
目录 概述|背景 报错解析 解决方法 IDEA配置解决 Pom配置插件解决 概述|背景 报错发生背景:在SpringBoot项目中引入Lombok依赖并使用后出现"找不到符号"的问题。 本文讨论在上述背景下发生的报错原因和解决办法,如果仅为了解决BUG不论原…...
差速驱动机器人MPC算法实现-C++
差速驱动机器人,其运动学模型需要考虑线速度和角速度。MPC(模型预测控制)需要建立预测模型,并在每个控制周期内求解优化问题。 差速驱动机器人的运动学方程通常包括位置(x, y)和航向角θ,线速度…...
将仓库A分支同步到仓库B分支,并且同步commit提交
一、 问题 有一仓库A 和 一仓库B, 需要将仓库A分支a1所有提交同步推送到仓库B分支b1上 二、 解决 2.1、 首先需要仓库A、仓库B的权限, 2.2、将仓库A clone到本地, 进入A目录,并且切换到a1分支 cd A ## A 为A仓库clone到本地代…...
kafka生产者之发送模式与ACK
文章目录 Kafka的发送模式Kafka的ack机制发送模式与ack的关联重试次数总结 在Kafka中,发送模式与ack机制紧密相关,它们共同影响着消息发送的可靠性和性能。 Kafka的发送模式 发后即忘(Fire and Forget):生产者发送消息…...
C++字符串相关内容
字符串 字符串,本质上是一个接一个字符的一组字符。字母、数字、符号等。 const char* 字符串名 字符后面会有一个空终止符,为0。 字符串从指针的内存地址开始,然后继续下去,直到它碰到0,然后意识到字符串终止了。 …...
Windows Docker笔记-Docker拉取镜像
通过在前面的章节《安装docker》中,了解并安装成功了Docker,本章讲述如何使用Docker拉取镜像。 使用Docker,主要是想要创建并运行Docker容器,而容器又要根据Docker镜像来创建,那么首当其冲,必须要先有一个…...
穷举vs暴搜vs深搜vs回溯vs剪枝系列一>黄金矿工
目录 决策树:代码设计代码: 决策树: 代码设计 代码: class Solution {boolean[][] vis;int ret,m,n;public int getMaximumGold(int[][] grid) {m grid.length;n grid[0].length;vis new boolean[m][n]; for(int i 0; i <…...
