RPC与 Thread 知识点全面总结

一、Thread(线程)核心知识点

1. 线程的本质与核心概念

  • 定义:线程是进程内的最小执行单元,是操作系统调度的基本单位(CPU 调度和分配的最小单位),共享所属进程的资源(内存空间、文件句柄等),但拥有独立的程序计数器(PC)、栈空间和寄存器集合。

  • 与进程的区别

    维度 线程(Thread) 进程(Process)
    资源分配 共享进程资源,无独立地址空间 拥有独立地址空间和资源(资源分配单位)
    调度成本 切换成本低(仅切换寄存器 / 栈) 切换成本高(需切换地址空间)
    通信方式 直接访问共享内存(简单高效) 需通过 IPC(管道、消息队列等)
    生命周期 依赖进程,进程终止则线程终止 独立生命周期,可独立创建 / 终止
  • 核心属性

    • 状态:新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked/Waiting/Timed Waiting)、终止(Terminated)。
    • 优先级:操作系统通过优先级决定调度顺序(如 Java 中 1-10 级,默认 5 级),但依赖 OS 调度策略(非绝对优先)。
    • daemon 线程:后台线程(如垃圾回收线程),进程中所有非 daemon 线程结束后,daemon 线程自动终止。

2. 线程的创建与启动方式

(1)基础创建方式(3 种)
  • 继承 Thread 类 :重写 run() 方法(线程执行逻辑),调用 start() 方法启动(底层调用 start0() native 方法,由 OS 创建线程并执行 run())。

    java

    复制代码
    class MyThread extends Thread {
        @Override
        public void run() {
            System.out.println("Thread 执行");
        }
    }
    // 启动
    new MyThread().start();
  • 实现 Runnable 接口 :解耦线程与任务,避免单继承限制,通过 Thread 包装类启动。

    java

    复制代码
    class MyRunnable implements Runnable {
        @Override
        public void run() {
            System.out.println("Runnable 执行");
        }
    }
    // 启动
    new Thread(new MyRunnable()).start();
  • 实现 Callable 接口 :支持返回结果和抛出异常,需配合 FutureTask(实现 RunnableFuture 接口)获取结果。

    java

    复制代码
    class MyCallable implements Callable<String> {
        @Override
        public String call() throws Exception {
            return "Callable 执行结果";
        }
    }
    // 启动并获取结果
    FutureTask<String> future = new FutureTask<>(new MyCallable());
    new Thread(future).start();
    String result = future.get(); // 阻塞等待结果
(2)线程池创建(推荐,避免频繁创建销毁线程)
  • 核心接口:ExecutorService,常用实现类:ThreadPoolExecutor(核心类)、FixedThreadPoolCachedThreadPool 等。

  • 优势:复用线程、控制并发数、管理线程生命周期(任务队列、拒绝策略等)。 java

    复制代码
    ExecutorService executor = Executors.newFixedThreadPool(5);
    executor.submit(new MyRunnable()); // 提交任务
    executor.shutdown(); // 关闭线程池(等待任务执行完)

3. 线程状态转换与控制方法

(1)状态转换核心逻辑
  • 新建(New)→ 就绪(Runnable):调用 start() 方法。
  • 就绪(Runnable)→ 运行(Running):OS 调度器分配 CPU 时间片。
  • 运行(Running)→ 就绪(Runnable):时间片用完或调用 yield()(礼让,释放 CPU,重新进入就绪队列)。
  • 运行(Running)→ 阻塞(Blocked):
    • 等待锁(synchronized 未获取到锁)→ Blocked 状态。
    • 调用 wait() → Waiting 状态(需被 notify()/notifyAll() 唤醒)。
    • 调用 sleep(long)/join()/wait(long) → Timed Waiting 状态(超时自动唤醒)。
  • 阻塞(Blocked)→ 就绪(Runnable):获取锁、被唤醒或超时。
  • 运行(Running)→ 终止(Terminated):run() 执行完毕或调用 stop()(已废弃,会导致资源泄露)。
(2)关键控制方法
方法 作用 注意事项
start() 启动线程,触发 OS 创建线程并执行 run() 不可重复调用(否则抛 IllegalThreadStateException)
run() 线程执行逻辑 直接调用仅为普通方法,不会创建新线程
sleep(long) 使线程休眠指定时间(毫秒),不释放锁 可被 interrupt() 中断,抛出 InterruptedException
wait() 使线程进入等待状态,释放锁 必须在 synchronized 块中调用,需被唤醒
notify() 唤醒一个等待的线程 wait(),需在 synchronized 块中调用
notifyAll() 唤醒所有等待的线程 同上
join() 等待目标线程执行完毕 t1.join(),当前线程等待 t1 结束
interrupt() 中断线程(设置中断标志) 不会直接终止线程,需配合 isInterrupted() 判断
yield() 礼让线程,释放 CPU 时间片 无强制效果,仅为调度器提示

4. 线程同步与并发安全

(1)并发安全问题根源
  • 多个线程同时操作共享资源(如共享变量、文件),导致指令重排、内存可见性问题、原子性问题(如 i++ 不是原子操作,包含读取、加 1、写入三步)。
(2)同步机制(解决并发安全)
  • synchronized 关键字

    • 作用范围:方法(实例方法锁当前对象,静态方法锁类对象)、代码块(锁指定对象 synchronized (lockObj) {})。
    • 原理:底层通过对象头的 Monitor 锁实现(重量级锁),JDK 6 后优化为偏向锁、轻量级锁、重量级锁的升级机制。
    • 特性:可重入(同一线程可多次获取同一锁)、非公平锁(默认)。
  • Lock 接口(JDK 5+)

    • 常用实现:ReentrantLock(可重入锁)、ReentrantReadWriteLock(读写锁)。

    • 优势:可中断、可设置超时、可实现公平锁、支持条件变量(Condition)。

    • 示例: java

      复制代码
      Lock lock = new ReentrantLock();
      lock.lock(); // 获取锁
      try {
          // 共享资源操作
      } finally {
          lock.unlock(); // 必须手动释放锁
      }
  • volatile 关键字

    • 作用:保证内存可见性(禁止 CPU 缓存,线程读取时直接从主内存获取)、禁止指令重排(如单例模式双重检查锁需用 volatile 修饰实例)。
    • 局限性:不保证原子性(如 volatile int i; i++ 仍不安全)。
  • 原子类(java.util.concurrent.atomic)

    • 基于 CAS(Compare And Swap)实现原子操作,无锁机制(高效)。
    • 常用类:AtomicIntegerAtomicLongAtomicReference(原子引用)。
    • 示例:atomicInteger.incrementAndGet()(原子自增,替代 i++)。
(3)线程通信方式
  • 共享内存:通过 synchronized/Lock 保护共享变量,线程读写变量实现通信(如生产者 - 消费者模式中通过共享队列通信)。
  • 消息传递:通过 wait()/notify()ConditionBlockingQueue(阻塞队列,如 ArrayBlockingQueue)实现。

5. 线程池核心原理(ThreadPoolExecutor)

  • 核心参数
    • corePoolSize:核心线程数(常驻线程,即使空闲也不销毁)。
    • maximumPoolSize:最大线程数(核心线程 + 临时线程的上限)。
    • keepAliveTime:临时线程空闲时间,超时后销毁。
    • workQueue:任务队列(核心线程满时,新任务入队)。
    • handler:拒绝策略(队列满且线程数达最大时,处理新任务的方式,如丢弃、抛出异常等)。
  • 执行流程
    1. 新任务提交 → 核心线程未满 → 创建核心线程执行任务。
    2. 核心线程满 → 任务队列未满 → 任务入队等待。
    3. 队列满 → 线程数未达最大 → 创建临时线程执行任务。
    4. 线程数达最大 → 执行拒绝策略。

6. 线程相关问题与最佳实践

  • 线程安全问题排查 :使用 jstack 查看线程堆栈(是否死锁、阻塞)、volatile/synchronized 误用排查。
  • 死锁条件:资源互斥、持有并等待、不可剥夺、循环等待 → 破坏任一条件即可避免(如按顺序获取锁)。
  • 最佳实践
    • 优先使用线程池,避免手动创建线程。
    • 避免使用 stop()/suspend()(已废弃),用 interrupt() 配合标志位终止线程。
    • 共享资源必须加锁,优先使用 concurrent 包下的工具类(如 ConcurrentHashMap 替代 HashMap)。
    • 避免线程阻塞时间过长(如 IO 操作需设置超时)。

二、RPC(远程过程调用)核心知识点

1. RPC 的本质与核心概念

  • 定义:RPC(Remote Procedure Call,远程过程调用)是一种通信协议,允许一台机器(客户端)的程序调用另一台机器(服务端)的程序方法,就像调用本地方法一样,无需关注网络通信细节(如 Socket、序列化、寻址等)。

  • 核心目标:屏蔽远程调用的复杂性,让开发者专注于业务逻辑("远程调用本地化")。

  • 与本地调用的区别

    维度 本地调用 RPC 调用
    执行位置 同一进程内 跨进程(跨机器)
    通信成本 无(直接调用内存中的方法) 有(网络传输、序列化 / 反序列化)
    可靠性 高(无网络问题) 低(需处理网络超时、重试、断连)
    参数传递 直接传递内存引用 需序列化(转为字节流)传输
    异常处理 仅处理本地异常 需处理网络异常、远程服务异常
  • 核心组件

    • 客户端(Client):发起远程调用的一方(调用者)。
    • 服务端(Server):提供远程方法的一方(被调用者)。
    • 客户端存根(Client Stub):封装网络通信、序列化、寻址逻辑,对客户端透明(如 Java 中的动态代理)。
    • 服务端存根(Server Stub):接收客户端请求、反序列化、调用本地方法、序列化结果返回。
    • 注册中心(Registry):服务端注册服务地址,客户端发现服务(如 ZooKeeper、Nacos、Eureka)。
    • 网络传输层:负责数据传输(如 TCP、HTTP/2)。
    • 序列化层:负责将对象转为字节流(如 Protobuf、JSON、Hessian)。

2. RPC 的核心流程(经典 5 步)

  1. 服务注册:服务端启动时,将自身提供的服务(如接口名、方法名、IP: 端口)注册到注册中心。
  2. 服务发现:客户端启动时,从注册中心订阅所需服务,获取服务端的地址列表(IP: 端口)。
  3. 请求序列化:客户端调用远程方法时,通过 Stub(动态代理)将方法名、参数等信息序列化为字节流。
  4. 网络传输:客户端通过网络传输层(如 TCP)将序列化后的请求发送到服务端。
  5. 响应处理
    • 服务端 Stub 接收请求,反序列化为方法调用信息。
    • 服务端调用本地对应的方法,执行业务逻辑。
    • 服务端将执行结果序列化后,通过网络返回给客户端。
    • 客户端 Stub 反序列化响应结果,返回给客户端程序(调用者)。

3. RPC 的关键技术点

(1)服务注册与发现
  • 核心作用:解决服务端地址动态变化的问题(如服务扩容、下线),让客户端无需硬编码服务端地址。

  • 常用注册中心

    注册中心 特点 适用场景
    ZooKeeper 强一致性、支持 Watcher 机制(监听服务变化) 分布式系统(如 Dubbo 默认支持)
    Nacos 支持 CP/AP 模式、动态配置、服务发现一体化 微服务架构(Spring Cloud 生态)
    Eureka AP 模式(高可用)、自我保护机制 Spring Cloud 早期生态(已停更)
  • 服务发现流程:客户端定时从注册中心拉取服务地址列表,或注册中心推送地址变化(如 ZooKeeper 的 Watcher)。

(2)序列化与反序列化
  • 核心作用:将内存中的对象(方法名、参数、结果)转为字节流(网络传输),或反之。

  • 关键要求:高效(序列化后体积小、速度快)、跨语言兼容、安全(无漏洞)。

  • 常用序列化协议

    协议 特点 适用场景
    Protobuf 二进制、体积小、速度快、跨语言 高性能 RPC(如 gRPC 默认协议)
    JSON 文本格式、可读性强、跨语言 轻量级 RPC(如 HTTP 接口)
    Hessian 二进制、支持 Java 对象序列化 Java 生态 RPC(如 Dubbo 支持)
    Thrift 跨语言、支持多种传输协议(TCP/HTTP) 跨语言 RPC(如 Facebook 内部使用)
(3)网络传输
  • 核心要求:低延迟、高吞吐量、可靠性(如重传、超时处理)。
  • 常用传输协议
    • TCP:面向连接、可靠传输、字节流(RPC 主流选择,如 Dubbo、gRPC)。
    • HTTP/2:多路复用、头部压缩、二进制帧(如 gRPC 支持,适合浏览器 / 服务端通信)。
    • UDP:无连接、不可靠、低延迟(适合实时性要求高的场景,如视频通话)。
  • 传输框架:Netty(Java 生态主流,基于 NIO 的高性能网络框架,支持 TCP/UDP/HTTP 等)。
(4)动态代理(客户端 Stub 实现)
  • 核心作用:封装 RPC 调用的底层逻辑(序列化、网络传输、反序列化),让客户端调用远程方法像调用本地方法一样。
  • Java 中实现方式
    • JDK 动态代理:基于接口,只能代理接口(如 Dubbo 早期默认)。
    • CGLIB 动态代理:基于类,可代理无接口的类(性能略高于 JDK 动态代理)。
  • 示例逻辑:客户端通过代理类调用方法时,代理类自动执行 "序列化请求 → 网络发送 → 接收响应 → 反序列化" 流程。
(5)负载均衡
  • 核心作用:客户端从服务端地址列表中选择一个节点发起调用,避免单节点压力过大。
  • 常用负载均衡算法
    • 随机算法(Random):随机选择节点(简单高效)。
    • 轮询算法(Round Robin):按顺序循环选择(均匀分配)。
    • 加权轮询(Weighted Round Robin):按节点权重分配(权重高的节点承担更多请求)。
    • 一致性哈希(Consistent Hash):解决节点扩容 / 下线时的缓存命中率问题(如分布式缓存)。
(6)容错机制
  • 核心作用:处理 RPC 调用中的异常(如网络超时、服务端宕机),保证系统可用性。
  • 常用容错策略
    • 重试(Retry):调用失败后,重试其他服务端节点(需避免幂等性问题)。
    • 超时控制(Timeout):设置调用超时时间,避免客户端无限阻塞。
    • 降级(Degrade):服务不可用时,返回默认值或降级逻辑(如返回缓存数据)。
    • 熔断(Circuit Breaker):多次调用失败后,暂时断开调用(如 Hystrix/Resilience4j),避免雪崩效应。

4. 主流 RPC 框架对比

框架 语言生态 核心特点 适用场景
Dubbo Java 为主(支持多语言) 高性能、丰富的负载均衡 / 容错 / 注册中心支持 Java 微服务架构(阿里开源)
gRPC 跨语言(Java/Go/C++ 等) 基于 HTTP/2 + Protobuf、多路复用、流式调用 跨语言、高性能场景(Google 开源)
Thrift 跨语言(多语言支持) 支持多种传输协议 / 序列化协议、轻量级 跨语言 RPC(Facebook 开源)
Spring Cloud OpenFeign Java(Spring 生态) 基于 HTTP 协议、声明式调用、与 Spring 无缝集成 Spring Cloud 微服务(轻量级 RPC)

5. RPC 与 HTTP 接口的区别

  • 传输协议:RPC 可基于 TCP/HTTP/2,HTTP 接口基于 HTTP(1.1/2)。
  • 序列化:RPC 支持高效二进制序列化(如 Protobuf),HTTP 接口多使用 JSON(文本序列化,体积大)。
  • 性能:RPC 性能更高(TCP 长连接、二进制序列化、无 HTTP 头部开销),HTTP 接口更通用(浏览器可调用)。
  • 易用性:RPC 需依赖框架(如 Dubbo),HTTP 接口可通过 Postman 直接调试,开发成本低。
  • 适用场景
    • RPC:内部服务间高频调用(如微服务内部通信)。
    • HTTP 接口:对外提供服务(如开放平台 API)、跨系统轻量级通信。

6. RPC 常见问题与最佳实践

  • 问题 1:序列化兼容性:不同版本的服务端 / 客户端序列化协议不一致 → 统一序列化协议版本(如 Protobuf 字段增加时保留字段号)。
  • 问题 2:网络超时与重试:重试导致重复请求 → 确保接口幂等性(如使用唯一请求 ID 去重)。
  • 问题 3:服务注册中心高可用:注册中心单点故障 → 部署集群(如 ZooKeeper 集群、Nacos 集群)。
  • 问题 4:负载均衡不均:部分节点压力过大 → 选择合适的负载均衡算法(如加权轮询),结合节点健康检查。
  • 最佳实践
    • 优先使用成熟 RPC 框架(避免重复造轮子)。
    • 接口设计需考虑幂等性(支持重试)。
    • 关键服务需部署集群,配合熔断 / 降级机制。
    • 监控 RPC 调用(如调用成功率、延迟、超时率)。

三、Thread 与 RPC 的关联

  1. RPC 调用的线程模型
    • 服务端:接收客户端请求后,通常通过线程池处理(如 Dubbo 的 FixedThreadPool),避免单线程阻塞导致无法处理多请求。
    • 客户端:发起 RPC 调用时,可选择同步(当前线程阻塞等待响应)或异步(线程池异步处理响应,不阻塞当前线程)。
  2. 并发控制
    • 服务端方法可能被多个 RPC 请求并发调用,需通过 synchronized/Lock 保证共享资源安全(与 Thread 的并发安全逻辑一致)。
  3. 性能优化
    • 线程池参数优化(如核心线程数、队列大小)直接影响 RPC 服务的吞吐量和响应速度。
    • RPC 框架的网络传输层(如 Netty)基于 NIO 模型,通过少量线程管理大量连接(减少线程切换开销)。

总结

  • Thread 是并发编程的基础,核心解决 "同一进程内多任务并行执行" 问题,重点关注线程创建、状态转换、同步安全、线程池管理。
  • RPC 是分布式系统的通信基础,核心解决 "跨进程 / 跨机器的方法调用本地化" 问题,重点关注服务注册发现、序列化、网络传输、容错机制。
  • 两者关联紧密:RPC 框架的底层实现依赖线程模型(如线程池处理请求),而 RPC 服务的并发安全又依赖线程同步机制。掌握两者的核心知识点,是构建高性能、高可用并发 / 分布式系统的关键。
相关推荐
小码过河.2 小时前
设计模式——解释器模式
java·设计模式·解释器模式
君爱学习2 小时前
MySQL - 基础增删查改
java
小y要自律2 小时前
08 string容器 - 字符串比较
开发语言·c++·stl
进击的小头2 小时前
移动平均滤波器:从原理到DSP ADC采样实战(C语言实现)
c语言·开发语言·算法
漂洋过海的鱼儿2 小时前
Qt--元对象系统
开发语言·数据库·qt
cyforkk2 小时前
05、Java 基础硬核复习:数组的本质与面试考点
java·开发语言·面试
csbysj20202 小时前
jQuery Growl:实现优雅的通知效果
开发语言
历程里程碑2 小时前
双指针--双数之和
开发语言·数据结构·c++·算法·排序算法·哈希算法·散列表
txwtech2 小时前
第24篇 vs2019QT QChart* chart = new QChart()发生访问冲突
开发语言·qt