一、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++仍不安全)。
- 作用:保证内存可见性(禁止 CPU 缓存,线程读取时直接从主内存获取)、禁止指令重排(如单例模式双重检查锁需用
-
原子类(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:拒绝策略(队列满且线程数达最大时,处理新任务的方式,如丢弃、抛出异常等)。
- 执行流程 :
- 新任务提交 → 核心线程未满 → 创建核心线程执行任务。
- 核心线程满 → 任务队列未满 → 任务入队等待。
- 队列满 → 线程数未达最大 → 创建临时线程执行任务。
- 线程数达最大 → 执行拒绝策略。
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 步)
- 服务注册:服务端启动时,将自身提供的服务(如接口名、方法名、IP: 端口)注册到注册中心。
- 服务发现:客户端启动时,从注册中心订阅所需服务,获取服务端的地址列表(IP: 端口)。
- 请求序列化:客户端调用远程方法时,通过 Stub(动态代理)将方法名、参数等信息序列化为字节流。
- 网络传输:客户端通过网络传输层(如 TCP)将序列化后的请求发送到服务端。
- 响应处理 :
- 服务端 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 的关联
- RPC 调用的线程模型 :
- 服务端:接收客户端请求后,通常通过线程池处理(如 Dubbo 的
FixedThreadPool),避免单线程阻塞导致无法处理多请求。 - 客户端:发起 RPC 调用时,可选择同步(当前线程阻塞等待响应)或异步(线程池异步处理响应,不阻塞当前线程)。
- 服务端:接收客户端请求后,通常通过线程池处理(如 Dubbo 的
- 并发控制 :
- 服务端方法可能被多个 RPC 请求并发调用,需通过
synchronized/Lock保证共享资源安全(与 Thread 的并发安全逻辑一致)。
- 服务端方法可能被多个 RPC 请求并发调用,需通过
- 性能优化 :
- 线程池参数优化(如核心线程数、队列大小)直接影响 RPC 服务的吞吐量和响应速度。
- RPC 框架的网络传输层(如 Netty)基于 NIO 模型,通过少量线程管理大量连接(减少线程切换开销)。
总结
- Thread 是并发编程的基础,核心解决 "同一进程内多任务并行执行" 问题,重点关注线程创建、状态转换、同步安全、线程池管理。
- RPC 是分布式系统的通信基础,核心解决 "跨进程 / 跨机器的方法调用本地化" 问题,重点关注服务注册发现、序列化、网络传输、容错机制。
- 两者关联紧密:RPC 框架的底层实现依赖线程模型(如线程池处理请求),而 RPC 服务的并发安全又依赖线程同步机制。掌握两者的核心知识点,是构建高性能、高可用并发 / 分布式系统的关键。