Java 多线程编程:核心原理、同步机制与高并发详解

        

目录

        

一、核心概念

1. 进程 vs 线程

2. 并行 vs 并发

3. 线程状态

4. 状态转换图

二、线程创建与启动

1、继承 Thread 类

2、实现 Runnable 接口

3、使用 Callable 和 Future

4、三种方式的对比

三、线程安全与同步机制

1. 线程安全问题

2. synchronized 关键字

3. ReentrantLock(JUC 包)

4. 原子类(AtomicXXX)

四、线程间协作

1. wait()/notify()/notifyAll()

2. Condition 接口

五、线程池(ExecutorService)

1. 线程池的优势

2.线程池的五种状态

3. 线程池状态转换

4、核心类:ThreadPoolExecutor

5、线程池的创建方式

1. 使用 Executors 工厂类(简化但有局限性)

FixedThreadPool(固定大小线程池):

CachedThreadPool(可缓存线程池):

ScheduledThreadPool(定时任务线程池):

SingleThreadExecutor(单线程池):

2. 自定义 ThreadPoolExecutor(推荐)

3、拒绝策略(RejectedExecutionHandler)

4、自定义策略

5、线程池的生命周期管理

6.最佳实践与注意事项

1. 线程数配置策略

2. 避免使用无界队列

3. 优先使用线程池而非手动创建线程

4. 异常处理

7、典型应用场景

六、高级工具类(JUC 包)

1. CountDownLatch

2. CyclicBarrier

3. Semaphore

七、线程安全集合类

1. 传统集合的线程安全问题

2. JUC 包中的线程安全集合

八、多线程设计模式

1. 生产者 - 消费者模式

2. 单例模式(双重检查锁)

九、线程调试与性能优化

1. 线程调试工具

2. 性能优化建议

十、注意事项


一、核心概念

1. 进程 vs 线程
  • 进程:程序在操作系统中的一次执行过程,是系统进行资源分配和调度的基本单位。
  • 线程:进程中的一个执行单元,是 CPU 调度和分派的基本单位。一个进程可以包含多个线程。
2. 并行 vs 并发
  • 并行:多个任务在同一时刻同时执行(多核 CPU)。
  • 并发:多个任务在同一时间段内交替执行(单核 CPU 通过时间片切换实现)。
3. 线程状态

Java 线程有 6 种状态(Thread.State):

状态名称描述
NEW线程对象已创建,但尚未调用start()方法。
RUNNABLE线程正在 Java 虚拟机中执行,或者准备就绪等待 CPU 时间片。
BLOCKED线程正在等待获取监视器锁(如synchronized块),处于阻塞状态。
WAITING线程无限期等待另一个线程执行特定操作(如调用wait()join())。
TIMED_WAITING线程在指定时间内等待另一个线程执行操作(如Thread.sleep())。
TERMINATED线程已执行完毕,终止运行。
4. 状态转换图

二、线程创建与启动

 在 Java 中,创建线程主要有三种方式,分别是继承Thread类、实现Runnable接口以及使用CallableFuture。下面将详细介绍这三种方式及其区别。

1、继承 Thread 类

步骤

  1. 定义一个类继承自Thread类。
  2. 重写run()方法,在该方法中定义线程要执行的任务。
  3. 创建该类的实例,并调用start()方法启动线程。

示例代码

public class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("继承Thread类创建的线程正在执行,线程名:" + Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程执行完毕");
    }

    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start(); // 启动线程
        System.out.println("主线程继续执行");
    }
}

特点

  • 优点:实现简单,直接通过继承Thread类并重写run()方法即可。
  • 缺点:由于 Java 是单继承的,继承了Thread类后就无法再继承其他类,会导致类的扩展性受限。
2、实现 Runnable 接口

步骤

  1. 定义一个类实现Runnable接口。
  2. 实现run()方法,在该方法中定义线程要执行的任务。
  3. 创建该类的实例,并将其作为参数传递给Thread类的构造函数。
  4. 调用Thread实例的start()方法启动线程。

示例代码

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("实现Runnable接口创建的线程正在执行,线程名:" + Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程执行完毕");
    }

    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start(); // 启动线程
        System.out.println("主线程继续执行");
    }
}

特点

优点:避免了单继承的限制,一个类可以在实现Runnable接口的同时继承其他类,提高了类的扩展性。适合多个线程共享同一个资源的场景,例如多个线程同时操作同一个银行账户。

  • 缺点:代码相对复杂一些,需要创建Runnable实现类的实例并将其传递给Thread类。
3、使用 Callable 和 Future

步骤

  1. 定义一个类实现Callable接口,指定返回值的类型。
  2. 实现call()方法,在该方法中定义线程要执行的任务,并返回结果。
  3. 创建ExecutorService线程池。
  4. 调用线程池的submit()方法提交Callable任务,并获取Future对象。
  5. 调用Future对象的get()方法获取线程执行的结果。

示例代码

import java.util.concurrent.*;

public class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        System.out.println("实现Callable接口创建的线程正在执行,线程名:" + Thread.currentThread().getName());
        Thread.sleep(1000);
        return 100; // 返回计算结果
    }

    public static void main(String[] args) {
        // 创建固定大小的线程池
        ExecutorService executor = Executors.newFixedThreadPool(1);
        // 提交Callable任务
        Future<Integer> future = executor.submit(new MyCallable());
        
        try {
            // 获取线程执行结果(可能会阻塞,直到任务完成)
            Integer result = future.get();
            System.out.println("线程执行结果:" + result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        } finally {
            // 关闭线程池
            executor.shutdown();
        }
        System.out.println("主线程继续执行");
    }
}

特点

  • 优点:Callable接口的call()方法可以返回结果,并且可以抛出异常,这是Runnable接口所不具备的功能。通过Future对象可以方便地获取线程的执行结果,还可以检查线程是否完成、取消线程等操作。
  • 缺点:代码复杂度较高,需要使用线程池来执行任务,适合需要返回结果的异步任务场景。
4、三种方式的对比
方式继承 Thread 类实现 Runnable 接口实现 Callable 接口
启动方式直接调用start()方法需要创建Thread对象需要通过线程池执行
是否支持返回结果是(通过Future获取)
是否支持异常抛出
是否受单继承限制
适用场景简单的线程任务共享资源的多线程任务需要返回结果的异步任务
  • 如果需要线程执行的任务简单,且不需要返回结果,可以选择继承Thread类的方式。
  • 如果需要多个线程共享同一个资源,或者为了避免单继承的限制,建议选择实现Runnable接口的方式。
  • 如果线程执行的任务需要返回结果,或者需要抛出异常,那么应该选择实现Callable接口的方式,并结合线程池使用。

三、线程安全与同步机制

1. 线程安全问题

当多个线程同时访问共享资源时,可能出现以下问题:

  • 竞态条件(Race Condition):多个线程对共享数据的读写操作导致结果不确定。
  • 内存可见性问题:一个线程修改了共享变量,其他线程可能看不到最新值。
2. synchronized 关键字
  • 同步方法
    public synchronized void increment() {
        count++;
    }
    
  • 同步代码块
    public void increment() {
        synchronized (this) {
            count++;
        }
    }
    
3. ReentrantLock(JUC 包)
import java.util.concurrent.locks.ReentrantLock;

public class Counter {
    private final ReentrantLock lock = new ReentrantLock();
    private int count = 0;

    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }
}
4. 原子类(AtomicXXX)
import java.util.concurrent.atomic.AtomicInteger;

public class AtomicCounter {
    private final AtomicInteger count = new AtomicInteger(0);

    public void increment() {
        count.incrementAndGet(); // 原子操作
    }
}

四、线程间协作

1. wait()/notify()/notifyAll()
public class ProducerConsumer {
    private final Object lock = new Object();
    private int data = 0;
    private boolean available = false;

    public void produce(int value) throws InterruptedException {
        synchronized (lock) {
            while (available) {
                lock.wait(); // 等待消费者取走数据
            }
            data = value;
            available = true;
            lock.notifyAll(); // 通知消费者数据已就绪
        }
    }

    public int consume() throws InterruptedException {
        synchronized (lock) {
            while (!available) {
                lock.wait(); // 等待生产者生产数据
            }
            available = false;
            lock.notifyAll(); // 通知生产者数据已取走
            return data;
        }
    }
}
2. Condition 接口
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ConditionExample {
    private final Lock lock = new ReentrantLock();
    private final Condition notFull = lock.newCondition();
    private final Condition notEmpty = lock.newCondition();
    private final java.util.LinkedList<Integer> buffer = new java.util.LinkedList<>();
    private static final int MAX_SIZE = 10;

    public void put(int value) throws InterruptedException {
        lock.lock();
        try {
            while (buffer.size() == MAX_SIZE) {
                notFull.await(); // 缓冲区满,等待
            }
            buffer.add(value);
            notEmpty.signal(); // 通知消费者
        } finally {
            lock.unlock();
        }
    }

    public int take() throws InterruptedException {
        lock.lock();
        try {
            while (buffer.isEmpty()) {
                notEmpty.await(); // 缓冲区空,等待
            }
            int value = buffer.poll();
            notFull.signal(); // 通知生产者
            return value;
        } finally {
            lock.unlock();
        }
    }
}

五、线程池(ExecutorService)

线程池可以对线程进行统一管理和复用,避免频繁创建和销毁线程带来的性能开销。Java 通过ExecutorService接口Executors工厂类来创建线程池。

1. 线程池的优势
  • 降低资源消耗
    避免频繁创建和销毁线程,重复利用已创建的线程。
  • 控制并发数量
    通过设置核心线程数、最大线程数等参数,防止因线程过多导致的资源竞争和阻塞。
  • 简化线程管理
    提供统一的任务提交接口(如submit()),并支持任务优先级、定时任务等高级功能。
  • 提高响应速度
    线程池中的线程可直接复用,无需等待新线程创建。
2.线程池的五种状态

1. RUNNING(运行中)

  • 状态值111(二进制,高 3 位)。
  • 含义
    • 线程池正常运行,接受新任务,并处理阻塞队列中的任务。
    • 初始状态即为 RUNNING
  • 转换来源
    • 新建线程池时自动进入此状态。

2. SHUTDOWN(关闭中)

  • 状态值000(二进制,高 3 位)。
  • 含义
    • 不再接受新任务,但会继续处理阻塞队列中已有的任务
    • 调用 shutdown() 方法会触发此状态转换。
  • 转换来源
    • 从 RUNNING 状态调用 shutdown()

3. STOP(停止中)

  • 状态值001(二进制,高 3 位)。
  • 含义
    • 立即停止线程池:
      • 中断所有正在执行的任务
      • 丢弃阻塞队列中未处理的任务
    • 调用 shutdownNow() 方法会触发此状态转换。
  • 转换来源
    • 从 RUNNING 或 SHUTDOWN 状态调用 shutdownNow()

4. TIDYING(整理中,过渡状态)

  • 状态值010(二进制,高 3 位)。
  • 含义
    • 所有任务(包括正在执行的和队列中的)已终止,工作线程数为 0
    • 即将进入 TERMINATED 状态前的过渡状态。
  • 触发条件
    • 当 SHUTDOWN 或 STOP 状态下,任务队列和工作线程均为空时,自动进入此状态。

5. TERMINATED(终止)

  • 状态值011(二进制,高 3 位)。
  • 含义
    • 线程池已完全终止,所有资源已释放。
  • 触发条件
    • 当 TIDYING 状态下,执行完终止钩子函数(terminated() 方法)后,进入此状态。
3. 线程池状态转换

4、核心类:ThreadPoolExecutor

Java 线程池的核心实现类是ThreadPoolExecutor,通过构造方法可自定义参数,灵活控制线程池行为。

构造方法参数详解:

public ThreadPoolExecutor(
    int corePoolSize,          // 核心线程数:线程池长期维持的最小线程数,即使空闲也不会销毁
    int maximumPoolSize,       // 最大线程数:线程池允许创建的最大线程数
    long keepAliveTime,        // 存活时间:非核心线程空闲时的存活时长
    TimeUnit unit,             // 时间单位
    BlockingQueue<Runnable> workQueue, // 任务队列:存放待执行的任务
    ThreadFactory threadFactory, // 线程工厂:创建线程的工厂(可自定义线程名、优先级等)
    RejectedExecutionHandler handler // 拒绝策略:任务无法执行时的处理逻辑
)

参数配置逻辑:

  1. 任务提交流程

    • 当线程数 < corePoolSize:创建新线程执行任务。
    • 当线程数 ≥ corePoolSize:任务加入workQueue队列。
    • 当队列已满且线程数 < maximumPoolSize:创建非核心线程执行任务。
    • 当队列已满且线程数 ≥ maximumPoolSize:触发拒绝策略
  2. 队列类型选择

    • 有界队列(如 ArrayBlockingQueue):避免内存溢出,需配合拒绝策略使用。
    • 无界队列(如 LinkedBlockingQueue)maximumPoolSize参数失效(线程数不超过corePoolSize),可能导致 OOM。
    • 优先队列(如 PriorityBlockingQueue):按任务优先级执行。
5、线程池的创建方式
1. 使用 Executors 工厂类(简化但有局限性)
  • FixedThreadPool(固定大小线程池)
    ExecutorService fixedPool = Executors.newFixedThreadPool(5); // corePoolSize = maxPoolSize = 5,使用无界队列
    

    适用场景:任务量已知、负载均衡的场景(如数据库连接池)。
    风险:无界队列可能导致内存溢出。

  • CachedThreadPool(可缓存线程池)
    ExecutorService cachedPool = Executors.newCachedThreadPool(); // corePoolSize=0,maxPoolSize=Integer.MAX_VALUE
    

    特点:线程空闲 60 秒后销毁,适合短时间任务(如 HTTP 请求处理)。
    风险:线程数可能无限增长,导致 OOM。

  • ScheduledThreadPool(定时任务线程池)
    ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(3);
    // 延迟1秒执行任务
    scheduledPool.schedule(() -> System.out.println("延迟执行"), 1, TimeUnit.SECONDS);
    
    适用场景:定时任务、周期性任务(如日志上报)。
  • SingleThreadExecutor(单线程池)
    ExecutorService singlePool = Executors.newSingleThreadExecutor(); // 保证任务按顺序执行
    
    适用场景:需要保证任务顺序执行或串行执行的场景。
2. 自定义 ThreadPoolExecutor(推荐)
// 自定义有界队列和拒绝策略
ThreadPoolExecutor executor = new ThreadPoolExecutor(
    2,                          // 核心线程数
    5,                          // 最大线程数
    30,                         // 非核心线程存活时间(秒)
    TimeUnit.SECONDS,
    new ArrayBlockingQueue<>(10), // 有界队列(容量10)
    Executors.defaultThreadFactory(),
    new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略:由调用者线程执行任务
);
3、拒绝策略(RejectedExecutionHandler)

当任务无法被线程池处理时(队列已满且线程数达上限),触发拒绝策略,共有 4 种内置策略:

策略名称行为描述
AbortPolicy(默认)抛出RejectedExecutionException,终止任务执行(需手动捕获异常)。
CallerRunsPolicy由提交任务的线程(如主线程)直接执行任务,降低提交任务的速度。
DiscardPolicy静默丢弃无法处理的任务,不抛出异常(可能丢失数据)。
DiscardOldestPolicy丢弃队列中最旧的任务,尝试提交当前任务(可能导致重要任务被丢弃)。
4、自定义策略
class CustomRejectedHandler implements RejectedExecutionHandler {
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        // 自定义处理逻辑(如记录日志、写入数据库)
        System.out.println("任务被拒绝:" + r.toString());
    }
}
5、线程池的生命周期管理
  1. 关闭线程池

    • shutdown():平滑关闭,不再接受新任务,但会等待已提交的任务执行完毕。
    • shutdownNow():立即关闭,尝试中断正在执行的任务,并返回等待执行的任务列表。
    executor.shutdown(); // 推荐使用此方式,避免任务丢失
    try {
        if (!executor.awaitTermination(60, TimeUnit.SECONDS)) { // 等待60秒
            executor.shutdownNow(); // 超时后强制关闭
        }
    } catch (InterruptedException e) {
        executor.shutdownNow();
    }
    
6.最佳实践与注意事项
1. 线程数配置策略
  • 计算密集型任务
    线程数 = CPU 核心数(如Runtime.getRuntime().availableProcessors()),避免上下文切换开销。
  • IO 密集型任务
    线程数 = CPU 核心数 × 2(或更高),因 IO 等待时线程可释放 CPU 资源。
    公式参考:线程数 = CPU 核心数 × (1 + 平均等待时间 / 平均工作时间)
  • 通用原则:
    健壮的线程池设计 = 有界队列 + 明确拒绝策略 + 合理线程数
2. 避免使用无界队列

Executors.newFixedThreadPool()newSingleThreadExecutor()默认使用LinkedBlockingQueue(无界队列),可能导致内存耗尽,建议自定义有界队列:

// 错误示例(无界队列)
ExecutorService badPool = Executors.newFixedThreadPool(5); 

// 正确示例(有界队列+拒绝策略)
ExecutorService goodPool = new ThreadPoolExecutor(
    5, 5, 0, TimeUnit.MILLISECONDS,
    new LinkedBlockingQueue<>(100), // 限制队列容量
    new ThreadPoolExecutor.AbortPolicy()
);
3. 优先使用线程池而非手动创建线程
// 推荐:使用线程池复用线程
executor.submit(() -> processTask());

// 不推荐:手动创建线程(缺乏管理)
new Thread(() -> processTask()).start();
4. 异常处理
  • 任务中抛出的未捕获异常会导致线程终止,需在run()call()中捕获异常,或通过Future处理:
    Future<?> future = executor.submit(() -> {
        if (hasError()) {
            throw new RuntimeException("任务失败"); // 需通过future.get()捕获ExecutionException
        }
    });
    try {
        future.get(); // 捕获异常
    } catch (ExecutionException e) {
        handleError(e.getCause());
    }
7、典型应用场景
  1. Web 服务器请求处理
    每个 HTTP 请求作为任务提交到线程池,避免为每个请求创建新线程。
  2. 异步数据处理
    批量处理数据(如日志解析、文件上传),利用线程池并行加速。
  3. 定时任务调度
    使用ScheduledThreadPoolExecutor实现定时统计、缓存更新等功能。
  4. 分布式系统中的任务队列
    结合消息中间件(如 Kafka),使用线程池消费消息,控制并发量。

六、高级工具类(JUC 包)

1. CountDownLatch
import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {
    public static void main(String[] args) throws InterruptedException {
        int threadCount = 3;
        CountDownLatch latch = new CountDownLatch(threadCount);

        for (int i = 0; i < threadCount; i++) {
            final int taskId = i;
            new Thread(() -> {
                System.out.println("任务" + taskId + "开始执行");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("任务" + taskId + "执行完毕");
                latch.countDown(); // 计数器减1
            }).start();
        }

        latch.await(); // 主线程等待所有任务完成
        System.out.println("所有任务已完成");
    }
}
2. CyclicBarrier
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierExample {
    public static void main(String[] args) {
        int parties = 3;
        CyclicBarrier barrier = new CyclicBarrier(parties, () -> {
            System.out.println("所有线程已到达屏障");
        });

        for (int i = 0; i < parties; i++) {
            final int threadId = i;
            new Thread(() -> {
                try {
                    System.out.println("线程" + threadId + "正在准备");
                    Thread.sleep(1000);
                    System.out.println("线程" + threadId + "已准备好,等待其他线程");
                    barrier.await(); // 等待其他线程
                    System.out.println("线程" + threadId + "继续执行");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}
3. Semaphore
import java.util.concurrent.Semaphore;

public class SemaphoreExample {
    public static void main(String[] args) {
        int permits = 2; // 允许2个线程同时访问
        Semaphore semaphore = new Semaphore(permits);

        for (int i = 0; i < 5; i++) {
            final int threadId = i;
            new Thread(() -> {
                try {
                    semaphore.acquire(); // 获取许可
                    System.out.println("线程" + threadId + "获取到许可,开始执行");
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release(); // 释放许可
                    System.out.println("线程" + threadId + "释放许可");
                }
            }).start();
        }
    }
}

七、线程安全集合类

1. 传统集合的线程安全问题
// 非线程安全的集合
List<String> list = new ArrayList<>();

// 线程安全的替代方案
List<String> synchronizedList = Collections.synchronizedList(new ArrayList<>());
2. JUC 包中的线程安全集合
// 高效的线程安全队列
BlockingQueue<String> blockingQueue = new LinkedBlockingQueue<>();

// 并发Map(分段锁实现)
ConcurrentHashMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();

八、多线程设计模式

1. 生产者 - 消费者模式

使用BlockingQueue实现:

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class ProducerConsumerPattern {
    private static final BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(10);

    public static void main(String[] args) {
        // 生产者线程
        Thread producer = new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    queue.put(i);
                    System.out.println("生产:" + i);
                    Thread.sleep(100);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        // 消费者线程
        Thread consumer = new Thread(() -> {
            try {
                while (true) {
                    Integer item = queue.take();
                    System.out.println("消费:" + item);
                    if (item == 9) break; // 结束条件
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        producer.start();
        consumer.start();
    }
}
2. 单例模式(双重检查锁)
public class Singleton {
    private static volatile Singleton instance; // 使用volatile保证可见性

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) { // 第一次检查
            synchronized (Singleton.class) {
                if (instance == null) { // 第二次检查
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

九、线程调试与性能优化

1. 线程调试工具
  • jstack:打印线程堆栈信息,用于分析死锁和阻塞问题。
  • VisualVM:可视化监控工具,查看线程状态、CPU 使用率等。
  • ThreadMXBean:编程方式获取线程信息。
2. 性能优化建议
  • 减少锁的粒度,避免synchronized块过大。
  • 使用无锁数据结构(如ConcurrentHashMap)替代传统同步集合。
  • 合理配置线程池大小:
    计算密集型任务:线程数 = CPU核心数 + 1
    IO密集型任务:线程数 = CPU核心数 * 2
    

十、注意事项

  1. 避免死锁
    1. 按相同顺序获取锁。
    2. 设置锁获取超时时间。

  2. 线程池使用规范
    1.避免使用无界队列(如Executors.newFixedThreadPool)。
    2. 明确拒绝策略(如CallerRunsPolicy)。

  3. 内存泄漏
    1. 静态集合持有线程引用。
    2. 未正确关闭资源(如线程池未调用shutdown())。

多线程编程是 Java 开发中的核心技能,掌握线程安全、同步机制和高级工具类的使用,能够显著提升系统的性能和稳定性。在实际开发中,应根据具体场景选择合适的并发模型和工具,避免过度设计。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值