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(核心类)、FixedThreadPool、CachedThreadPool 等。

  • 优势:复用线程、控制并发数、管理线程生命周期(任务队列、拒绝策略等)。 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)实现原子操作,无锁机制(高效)。
    • 常用类:AtomicInteger、AtomicLong、AtomicReference(原子引用)。
    • 示例:atomicInteger.incrementAndGet()(原子自增,替代 i++)。
(3)线程通信方式
  • 共享内存:通过 synchronized/Lock 保护共享变量,线程读写变量实现通信(如生产者 - 消费者模式中通过共享队列通信)。
  • 消息传递:通过 wait()/notify()、Condition、BlockingQueue(阻塞队列,如 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 服务的并发安全又依赖线程同步机制。掌握两者的核心知识点,是构建高性能、高可用并发 / 分布式系统的关键。
相关推荐
xcl09255 小时前
北京24小时自助健身房系统软件开发实战:从架构到部署全流程指南
java·spring boot·架构
一 乐6 小时前
动漫书销售商城|基于springboot + vue动漫书销售商城(源码+数据库+文档)
java·数据库·vue.js·spring boot·毕业设计
卓怡学长7 小时前
w214基于jsp知道特产网
java·intellij-idea
步行cgn7 小时前
Spring 注解使用详解
java·spring
一条小小yu7 小时前
Spring IoC的理解
java·后端·spring
落魄实习生7 小时前
Agent Scope Java 2.x 系列【7】工具使用
java·开发语言·ai
旺仔学长 哈哈7 小时前
springboot钓鱼爱好者交流平台APP设计与实现
java·spring boot·mysql·充电桩管理系统
茉莉玫瑰花茶8 小时前
GO [ 方法 ]
开发语言·后端·golang
狗凯之家源码网8 小时前
微博图床 PHP 系统搭建与实战应用指南
android·开发语言·php