线程池深度解析

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

一、为什么需要线程池?

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. 优雅处理:做好异常处理和优雅关闭

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

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

相关推荐
程序新视界26 分钟前
为什么不建议基于Multi-Agent来构建Agent工程?
人工智能·后端·agent
Victor35638 分钟前
Hibernate(29)什么是Hibernate的连接池?
后端
Victor35638 分钟前
Hibernate(30)Hibernate的Named Query是什么?
后端
源代码•宸1 小时前
GoLang八股(Go语言基础)
开发语言·后端·golang·map·defer·recover·panic
czlczl200209251 小时前
OAuth 2.0 解析:后端开发者视角的原理与流程讲解
java·spring boot·后端
颜淡慕潇1 小时前
Spring Boot 3.3.x、3.4.x、3.5.x 深度对比与演进分析
java·后端·架构
布列瑟农的星空1 小时前
WebAssembly入门(一)——Emscripten
前端·后端
小突突突3 小时前
Spring框架中的单例bean是线程安全的吗?
java·后端·spring
iso少年3 小时前
Go 语言并发编程核心与用法
开发语言·后端·golang
掘金码甲哥3 小时前
云原生算力平台的架构解读
后端