线程池深度解析

线程池深度解析:从使用到源码,再到生产实践

一、为什么需要线程池?

1.1 线程的代价

在开始讲解线程池之前,我们先看一个简单的例子:

java 复制代码
// 原始方式:每次请求都创建新线程
public void handleRequest(Request request) {
    new Thread(() -> {
        // 处理业务逻辑
        process(request);
    }).start();
}

这种方式有什么问题?

  • 创建成本高:每次创建线程需要分配内存、初始化栈(默认1MB)
  • 资源浪费:线程创建/销毁消耗CPU资源
  • 难以管理:线程数量无限制,容易导致系统崩溃

1.2 线程池的救赎

java 复制代码
// 使用线程池后的优雅方式
private ExecutorService threadPool = Executors.newFixedThreadPool(10);

public void handleRequest(Request request) {
    threadPool.execute(() -> {
        process(request);
    });
}

二、Java线程池家族

2.1 Executors工厂类

Java提供了4种常用的线程池:

java 复制代码
// 1. 固定大小线程池
ExecutorService fixedPool = Executors.newFixedThreadPool(10);
// 适用场景:已知并发量,需要控制资源

// 2. 单线程线程池
ExecutorService singlePool = Executors.newSingleThreadExecutor();
// 适用场景:任务需要顺序执行

// 3. 可缓存线程池
ExecutorService cachedPool = Executors.newCachedThreadPool();
// 适用场景:短期异步任务,线程数自动调整

// 4. 定时任务线程池
ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(5);
// 适用场景:延迟执行或周期性任务

2.2 这些"快捷方式"的真相

我们点开源码看看:

java 复制代码
// Executors.newFixedThreadPool源码
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(
        nThreads,                  // 核心线程数
        nThreads,                  // 最大线程数
        0L, TimeUnit.MILLISECONDS, // 空闲线程存活时间
        new LinkedBlockingQueue<Runnable>() // 无界队列
    );
}

注意坑点LinkedBlockingQueue默认是Integer.MAX_VALUE,任务堆积可能OOM!

三、深入ThreadPoolExecutor源码

3.1 核心参数解析

java 复制代码
public ThreadPoolExecutor(
    int corePoolSize,      // 核心线程数(长期存活的线程)
    int maximumPoolSize,   // 最大线程数
    long keepAliveTime,    // 空闲线程存活时间
    TimeUnit unit,         // 时间单位
    BlockingQueue<Runnable> workQueue,  // 工作队列
    ThreadFactory threadFactory,        // 线程工厂
    RejectedExecutionHandler handler    // 拒绝策略
)

3.2 线程池工作流程(源码级解析)

java 复制代码
// 简化版的execute方法流程
public void execute(Runnable command) {
    // 第一阶段:核心线程处理
    if (workerCount < corePoolSize) {
        if (addWorker(command, true))  // 创建核心线程
            return;
    }
    
    // 第二阶段:任务入队
    if (isRunning() && workQueue.offer(command)) {
        // 再次检查
        if (!isRunning() && remove(command))
            reject(command);
        else if (workerCount == 0)
            addWorker(null, false);
    }
    
    // 第三阶段:创建非核心线程
    else if (!addWorker(command, false))
        // 第四阶段:拒绝策略
        reject(command);
}

让我们用流程图更直观地理解:

markdown 复制代码
任务提交
    ↓
当前线程数 < corePoolSize?
    ├── 是 → 创建核心线程执行任务
    ↓
    └── 否 → 工作队列未满?
        ├── 是 → 任务入队等待
        ↓
        └── 否 → 当前线程数 < maximumPoolSize?
            ├── 是 → 创建临时线程执行
            ↓
            └── 否 → 执行拒绝策略

3.3 Worker内部类:线程池的真正执行者

java 复制代码
private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
    final Thread thread;  // 实际执行任务的线程
    Runnable firstTask;   // 初始任务
    
    Worker(Runnable firstTask) {
        this.firstTask = firstTask;
        this.thread = getThreadFactory().newThread(this);
    }
    
    public void run() {
        runWorker(this);  // 核心执行方法
    }
    
    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        
        while (task != null || (task = getTask()) != null) {
            // 获取锁,确保线程不会被其他任务中断
            w.lock();
            
            try {
                // 执行前的钩子方法
                beforeExecute(wt, task);
                
                try {
                    task.run();  // 执行任务!
                    afterExecute(task, null);
                } catch (Throwable ex) {
                    afterExecute(task, ex);
                    throw ex;
                }
            } finally {
                task = null;
                w.completedTasks++;  // 统计完成任务数
                w.unlock();
            }
        }
        
        processWorkerExit(w, completedAbruptly);
    }
}

3.4 getTask():线程如何获取任务

java 复制代码
private Runnable getTask() {
    boolean timedOut = false; // 上次poll是否超时
    
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);
        
        // 检查线程池状态
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }
        
        int wc = workerCountOf(c);
        
        // 判断是否允许超时回收线程
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
        
        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }
        
        try {
            // 关键:从队列取任务
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();
            if (r != null)
                return r;
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}

四、拒绝策略详解

4.1 Java内置的4种拒绝策略

java 复制代码
// 1. AbortPolicy(默认) - 直接抛出异常
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    throw new RejectedExecutionException("Task rejected");
}

// 2. CallerRunsPolicy - 让调用者线程执行
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    if (!e.isShutdown()) {
        r.run();  // 注意:是run()不是start()!
    }
}

// 3. DiscardPolicy - 默默丢弃
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    // 什么都不做,直接丢弃
}

// 4. DiscardOldestPolicy - 丢弃队列最老任务
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    if (!e.isShutdown()) {
        e.getQueue().poll();  // 丢弃队头
        e.execute(r);         // 尝试重新执行
    }
}

4.2 自定义拒绝策略实战

java 复制代码
@Component
public class CustomRejectPolicy implements RejectedExecutionHandler {
    
    private final MeterRegistry meterRegistry;
    
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        // 1. 记录指标
        meterRegistry.counter("threadpool.rejected.tasks").increment();
        
        // 2. 降级策略
        if (r instanceof ImportantTask) {
            // 重要任务:记录日志,等待重试
            log.warn("重要任务被拒绝,放入重试队列");
            retryQueue.offer((ImportantTask) r);
        } else {
            // 普通任务:异步持久化,稍后处理
            asyncSaveToDB(r);
        }
        
        // 3. 告警通知
        if (needAlert(executor)) {
            sendAlert(executor);
        }
    }
}

五、生产环境线程池配置方案

5.1 线程数计算公式

java 复制代码
// 根据任务类型设置线程数
public class ThreadPoolConfig {
    
    /**
     * CPU密集型任务(计算、加密等)
     * 线程数 = CPU核心数 + 1
     */
    public static ExecutorService cpuIntensivePool() {
        int cpuCores = Runtime.getRuntime().availableProcessors();
        return new ThreadPoolExecutor(
            cpuCores + 1,
            cpuCores + 1,
            0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<>(1000)
        );
    }
    
    /**
     * IO密集型任务(网络请求、数据库操作等)
     * 线程数 = CPU核心数 * (1 + 等待时间/计算时间)
     * 通常设置为 CPU核心数 * 2 ~ 5
     */
    public static ExecutorService ioIntensivePool() {
        int cpuCores = Runtime.getRuntime().availableProcessors();
        return new ThreadPoolExecutor(
            cpuCores * 2,
            cpuCores * 5,
            60L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(2000)
        );
    }
    
    /**
     * 混合型任务
     * 最佳线程数 = CPU核心数 * (1 + 阻塞系数)
     * 阻塞系数 = 阻塞时间 / (阻塞时间 + 计算时间)
     */
    public static ExecutorService mixedPool(double blockingCoefficient) {
        int cpuCores = Runtime.getRuntime().availableProcessors();
        int optimalThreads = (int) (cpuCores * (1 + blockingCoefficient));
        
        return new ThreadPoolExecutor(
            optimalThreads,
            optimalThreads * 2,
            30L, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(optimalThreads * 10)
        );
    }
}

5.2 Spring Boot中的最佳配置

yaml 复制代码
# application.yml
thread-pool:
  configs:
    order-process:
      core-size: 8
      max-size: 16
      queue-capacity: 100
      keep-alive-seconds: 60
      thread-name-prefix: order-
      reject-policy: CALLER_RUNS
      
    payment-callback:
      core-size: 4
      max-size: 8
      queue-capacity: 200
      thread-name-prefix: payment-
      reject-policy: ABORT
      
    report-generate:
      core-size: 2
      max-size: 4
      queue-capacity: 1000
      thread-name-prefix: report-
      allow-core-thread-timeout: true
java 复制代码
@Configuration
@EnableConfigurationProperties(ThreadPoolProperties.class)
public class ThreadPoolConfig {
    
    @Bean
    public Map<String, ExecutorService> threadPools(
            ThreadPoolProperties properties) {
        
        Map<String, ExecutorService> pools = new HashMap<>();
        
        properties.getConfigs().forEach((name, config) -> {
            // 动态参数配置
            DynamicThreadPoolExecutor executor = 
                new DynamicThreadPoolExecutor(
                    config.getCoreSize(),
                    config.getMaxSize(),
                    config.getKeepAliveSeconds(),
                    TimeUnit.SECONDS,
                    createQueue(config.getQueueCapacity()),
                    new CustomThreadFactory(config.getThreadNamePrefix()),
                    createRejectPolicy(config.getRejectPolicy())
                );
            
            // 允许核心线程超时
            if (config.isAllowCoreThreadTimeout()) {
                executor.allowCoreThreadTimeOut(true);
            }
            
            // 注册监控
            ThreadPoolMonitor.register(name, executor);
            
            pools.put(name, executor);
        });
        
        return pools;
    }
    
    private BlockingQueue<Runnable> createQueue(int capacity) {
        // 根据队列大小选择队列类型
        if (capacity <= 0) {
            return new SynchronousQueue<>();
        } else if (capacity <= 10000) {
            return new ArrayBlockingQueue<>(capacity);
        } else {
            return new LinkedBlockingQueue<>(capacity);
        }
    }
}

5.3 动态线程池:美团动态线程池实践

java 复制代码
@Component
public class DynamicThreadPoolExecutor extends ThreadPoolExecutor {
    
    private final String poolName;
    private final ThreadPoolProperties properties;
    
    public DynamicThreadPoolExecutor(String poolName, 
                                     ThreadPoolProperties properties) {
        super(properties.getCoreSize(), 
              properties.getMaxSize(),
              properties.getKeepAliveSeconds(), 
              TimeUnit.SECONDS,
              new ResizableCapacityLinkedBlockingQueue<>(
                  properties.getQueueCapacity()
              ));
        this.poolName = poolName;
        this.properties = properties;
        
        // 初始化监听
        initListeners();
    }
    
    /**
     * 动态调整核心线程数
     */
    public void setCorePoolSize(int corePoolSize) {
        if (corePoolSize >= 0 && 
            corePoolSize <= getMaximumPoolSize()) {
            super.setCorePoolSize(corePoolSize);
            
            // 记录变更
            log.info("线程池{}核心线程数调整为: {}", 
                     poolName, corePoolSize);
        }
    }
    
    /**
     * 动态调整最大线程数
     */
    public void setMaximumPoolSize(int maximumPoolSize) {
        if (maximumPoolSize >= getCorePoolSize()) {
            super.setMaximumPoolSize(maximumPoolSize);
            
            // 记录变更
            log.info("线程池{}最大线程数调整为: {}", 
                     poolName, maximumPoolSize);
        }
    }
    
    /**
     * 动态调整队列容量
     */
    public void setQueueCapacity(int capacity) {
        ResizableCapacityLinkedBlockingQueue<Runnable> queue = 
            (ResizableCapacityLinkedBlockingQueue<Runnable>) getQueue();
        queue.setCapacity(capacity);
        
        log.info("线程池{}队列容量调整为: {}", poolName, capacity);
    }
}

// 可调整容量的队列
public class ResizableCapacityLinkedBlockingQueue<E> 
        extends LinkedBlockingQueue<E> {
    
    private volatile int capacity;
    
    public ResizableCapacityLinkedBlockingQueue(int capacity) {
        super(capacity);
        this.capacity = capacity;
    }
    
    public synchronized void setCapacity(int newCapacity) {
        if (newCapacity <= 0) {
            throw new IllegalArgumentException();
        }
        
        // 调整容量
        this.capacity = newCapacity;
        
        // 如果新容量小于当前size,需要移除多余元素
        while (size() > newCapacity) {
            poll();
        }
    }
    
    @Override
    public boolean offer(E e) {
        // 动态判断是否接受新任务
        if (size() >= capacity) {
            return false;
        }
        return super.offer(e);
    }
}

六、线程池监控方案

6.1 监控指标体系

java 复制代码
@Component
public class ThreadPoolMonitor {
    
    private final MeterRegistry meterRegistry;
    
    public void monitor(String poolName, ThreadPoolExecutor executor) {
        // 1. 核心指标
        monitorCoreMetrics(poolName, executor);
        
        // 2. 队列指标
        monitorQueueMetrics(poolName, executor);
        
        // 3. 任务执行指标
        monitorTaskMetrics(poolName, executor);
        
        // 4. 线程状态指标
        monitorThreadMetrics(poolName, executor);
    }
    
    private void monitorCoreMetrics(String poolName, 
                                    ThreadPoolExecutor executor) {
        // 线程数指标
        Gauge.builder("threadpool.core.size", 
                     executor, ThreadPoolExecutor::getCorePoolSize)
            .tag("pool", poolName)
            .register(meterRegistry);
            
        Gauge.builder("threadpool.active.count",
                     executor, ThreadPoolExecutor::getActiveCount)
            .tag("pool", poolName)
            .register(meterRegistry);
            
        Gauge.builder("threadpool.largest.pool.size",
                     executor, ThreadPoolExecutor::getLargestPoolSize)
            .tag("pool", poolName)
            .register(meterRegistry);
    }
    
    private void monitorQueueMetrics(String poolName,
                                     ThreadPoolExecutor executor) {
        BlockingQueue<Runnable> queue = executor.getQueue();
        
        Gauge.builder("threadpool.queue.size",
                     queue, BlockingQueue::size)
            .tag("pool", poolName)
            .register(meterRegistry);
            
        Gauge.builder("threadpool.queue.remaining",
                     queue, q -> q.remainingCapacity())
            .tag("pool", poolName)
            .register(meterRegistry);
    }
}

6.2 可视化监控面板(Grafana)

json 复制代码
{
  "panels": [
    {
      "title": "线程池状态",
      "targets": [
        {
          "expr": "threadpool_active_count{pool=\"$pool\"}",
          "legendFormat": "活跃线程"
        },
        {
          "expr": "threadpool_core_size{pool=\"$pool\"}",
          "legendFormat": "核心线程"
        },
        {
          "expr": "threadpool_max_size{pool=\"$pool\"}",
          "legendFormat": "最大线程"
        }
      ]
    },
    {
      "title": "队列监控",
      "targets": [
        {
          "expr": "threadpool_queue_size{pool=\"$pool\"}",
          "legendFormat": "队列大小"
        },
        {
          "expr": "threadpool_queue_remaining{pool=\"$pool\"}",
          "legendFormat": "队列剩余容量"
        }
      ]
    }
  ]
}

6.3 告警规则配置

yaml 复制代码
# alert-rules.yml
groups:
  - name: threadpool_alerts
    rules:
      - alert: ThreadPoolQueueFull
        expr: threadpool_queue_remaining{pool="order-process"} == 0
        for: 1m
        annotations:
          summary: "订单线程池队列已满"
          description: "{{ $labels.pool }}队列已满,持续1分钟"
          
      - alert: ThreadPoolRejectedTasksHigh
        expr: rate(threadpool_rejected_tasks_total{pool="payment-callback"}[5m]) > 10
        for: 30s
        annotations:
          summary: "支付回调线程池拒绝任务过多"
          description: "{{ $labels.pool }}5分钟内拒绝任务超过10个"
          
      - alert: ThreadPoolActiveThreadsHigh
        expr: threadpool_active_count{pool="report-generate"} / threadpool_max_size{pool="report-generate"} > 0.8
        for: 2m
        annotations:
          summary: "报表生成线程池使用率过高"
          description: "{{ $labels.pool }}活跃线程数达到最大线程数的80%"

七、常见问题与解决方案

7.1 线程池中的线程异常怎么办?

java 复制代码
public class SafeThreadPoolExecutor extends ThreadPoolExecutor {
    
    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
        
        // 处理未捕获异常
        if (t == null && r instanceof Future<?>) {
            try {
                Future<?> future = (Future<?>) r;
                if (future.isDone()) {
                    future.get();
                }
            } catch (CancellationException ce) {
                t = ce;
            } catch (ExecutionException ee) {
                t = ee.getCause();
            } catch (InterruptedException ie) {
                Thread.currentThread().interrupt();
            }
        }
        
        if (t != null) {
            // 记录异常并采取恢复措施
            log.error("线程池任务执行异常", t);
            handleException(t);
        }
    }
    
    private void handleException(Throwable t) {
        // 1. 发送告警
        alertSystem.send(t);
        
        // 2. 重试机制
        if (canRetry(t)) {
            retryQueue.offer(currentTask);
        }
        
        // 3. 降级处理
        fallbackService.process(currentTask);
    }
}

7.2 如何优雅关闭线程池?

java 复制代码
@Component
public class ThreadPoolShutdownHandler {
    
    @PreDestroy
    public void shutdownAll() {
        // 1. 拒绝新任务
        threadPool.shutdown();
        
        try {
            // 2. 等待现有任务完成(最多等待30分钟)
            if (!threadPool.awaitTermination(30, TimeUnit.MINUTES)) {
                // 3. 强制关闭
                List<Runnable> unfinishedTasks = 
                    threadPool.shutdownNow();
                
                // 4. 处理未完成的任务
                handleUnfinishedTasks(unfinishedTasks);
                
                // 5. 再次等待
                if (!threadPool.awaitTermination(10, TimeUnit.MINUTES)) {
                    log.error("线程池无法正常关闭");
                }
            }
        } catch (InterruptedException e) {
            // 6. 重新中断
            threadPool.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
    
    private void handleUnfinishedTasks(List<Runnable> tasks) {
        // 持久化未完成任务
        tasks.forEach(task -> {
            if (task instanceof PersistableTask) {
                taskPersistenceService.save((PersistableTask) task);
            }
        });
    }
}

八、最佳实践总结

8.1 线程池配置清单

配置项 推荐值 说明
corePoolSize CPU核心数+1~核心数*2 根据任务类型调整
maximumPoolSize corePoolSize的2~4倍 为突发流量预留
keepAliveTime 30~60秒 避免频繁创建线程
workQueue ArrayBlockingQueue 有界队列,防止OOM
threadFactory 自定义命名 方便问题排查
rejectedPolicy CallerRunsPolicy 或自定义降级策略

8.2 线程池使用"七不要"

  1. 不要使用Executors的快捷方法
  2. 不要使用无界队列(LinkedBlockingQueue默认无界)
  3. 不要让任务抛出未捕获异常
  4. 不要在任务中无限循环而不检查中断
  5. 不要忘记关闭线程池
  6. 不要所有业务共用一个线程池
  7. 不要忽视线程池监控

8.3 推荐的线程池工具类

java 复制代码
public class ThreadPoolUtils {
    
    private static final Map<String, ExecutorService> POOLS = 
        new ConcurrentHashMap<>();
    
    /**
     * 获取或创建线程池
     */
    public static ExecutorService getOrCreate(String poolName,
                                              Supplier<ExecutorService> creator) {
        return POOLS.computeIfAbsent(poolName, k -> creator.get());
    }
    
    /**
     * 安全执行任务
     */
    public static <T> CompletableFuture<T> safeSubmit(
            ExecutorService executor, 
            Callable<T> task) {
        CompletableFuture<T> future = new CompletableFuture<>();
        
        executor.submit(() -> {
            try {
                T result = task.call();
                future.complete(result);
            } catch (Throwable t) {
                future.completeExceptionally(t);
                log.error("任务执行失败", t);
            }
        });
        
        return future;
    }
    
    /**
     * 优雅关闭所有线程池
     */
    @PreDestroy
    public static void shutdownAll() {
        POOLS.forEach((name, pool) -> {
            try {
                pool.shutdown();
                if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
                    pool.shutdownNow();
                }
            } catch (InterruptedException e) {
                pool.shutdownNow();
                Thread.currentThread().interrupt();
            }
        });
    }
}

九、进阶:虚拟线程(Java 21+)

Java 21引入了虚拟线程,这是对线程模型的革命性改进:

java 复制代码
// 创建虚拟线程
Thread virtualThread = Thread.ofVirtual()
    .name("virtual-thread-", 0)
    .start(() -> {
        // 任务逻辑
    });

// 使用虚拟线程的ExecutorService
ExecutorService virtualExecutor = Executors.newVirtualThreadPerTaskExecutor();

// 性能对比:10万个任务
long start = System.currentTimeMillis();

// 传统线程池:可能创建数千个平台线程
try (ExecutorService executor = Executors.newFixedThreadPool(200)) {
    for (int i = 0; i < 100_000; i++) {
        executor.submit(() -> {
            Thread.sleep(100);
            return "done";
        });
    }
}

// 虚拟线程:轻松处理
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
    for (int i = 0; i < 100_000; i++) {
        executor.submit(() -> {
            Thread.sleep(100);
            return "done";
        });
    }
}

虚拟线程的优势:

  • 轻量级:内存开销约2KB(平台线程约1MB)
  • 高并发:轻松支持数百万个虚拟线程
  • 兼容性:完全兼容现有Thread API

总结

线程池是Java并发编程的核心组件,正确使用线程池需要:

  1. 理解原理:掌握ThreadPoolExecutor的工作机制
  2. 合理配置:根据业务场景选择合适的参数
  3. 监控预警:建立完善的监控体系
  4. 动态调整:根据负载情况动态调整参数
  5. 优雅处理:做好异常处理和优雅关闭

记住:没有万能的线程池配置,只有最适合你业务场景的配置。通过监控、分析和调整,不断优化你的线程池配置,才能构建出稳定高效的系统。

希望这篇文章能帮助你全面掌握线程池!如果有任何问题,欢迎随时讨论。

相关推荐
sheji341610 分钟前
【开题答辩全过程】以 基于Spring Boot的化妆品销售系统的设计与实现为例,包含答辩的问题和答案
java·spring boot·后端
只是懒得想了11 分钟前
Go服务限流实战:基于golang.org/x/time/rate与uber-go/ratelimit的深度解析
开发语言·后端·golang
VX:Fegn08958 小时前
计算机毕业设计|基于ssm + vue超市管理系统(源码+数据库+文档)
前端·数据库·vue.js·spring boot·后端·课程设计
Java天梯之路13 小时前
Spring Boot 钩子全集实战(七):BeanFactoryPostProcessor详解
java·spring boot·后端
wr20051413 小时前
第二次作业,渗透
java·后端·spring
短剑重铸之日13 小时前
《SpringCloud实用版》生产部署:Docker + Kubernetes + GraalVM 原生镜像 完整方案
后端·spring cloud·docker·kubernetes·graalvm
爬山算法14 小时前
Hibernate(67)如何在云环境中使用Hibernate?
java·后端·hibernate
女王大人万岁15 小时前
Go标准库 io与os库详解
服务器·开发语言·后端·golang
露天赏雪15 小时前
Java 高并发编程实战:从线程池到分布式锁,解决生产环境并发问题
java·开发语言·spring boot·分布式·后端·mysql
短剑重铸之日16 小时前
《SpringCloud实用版》 Seata 分布式事务实战:AT / TCC / Saga /XA
后端·spring·spring cloud·seata·分布式事务