并发编程概述
-
并发编程的重要性
- 大厂招聘要求掌握多线程编程能力。
- 并发编程是升职加薪的重要技能之一。
- 并发提升响应速度、实现异步化、充分利用CPU资源。
-
单核与多核处理
- 即使在单核处理器上,也可通过并发实现多个任务交替执行。
- 多核处理器下,并发能真正实现并行执行。
-
QQ/微信并发示例
- 输入文字、发送消息、接收信息三者同时发生,体现并发特性。
- 若使用单线程,则需串行完成以上操作,用户体验差。
进程与线程概念解析
-
进程定义
- 进程是一个程序的实例,包含指令、内存管理、I/O操作等。
- 类比面向对象中类与对象的关系,程序是类,进程是对象。
- 进程是操作系统中资源分配的最小单位。
-
线程定义
- 线程是操作系统中CPU调度的最小单位。
- 一个进程中可以包含多个线程,但线程必须依附于进程存在。
- Linux早期无独立线程概念,后称为轻量级进程(LWP)。
-
Java中的线程机制
- JVM本身作为一个进程,内部自动启动多个线程,如主线程、Finalizer线程、Reference Handler线程等。
- 即使是最简单的Java程序,也会默认启动多个线程。
进程间通信方式
- 常见IPC机制
- 管道(Pipe):分为匿名管道(父子进程通信)与命名管道(任意两个进程通信)。
- 信号(Signal):用于向应用程序发送特定事件通知。
- 消息队列(Message Queue):类似MQ,用于进程间传递消息。
- 共享内存(Shared Memory):多个进程访问同一块内存区域,效率高但需同步控制。
- 信号量(Semaphore):用于控制对共享资源的访问。
- 套接字(Socket):支持跨机器通信,也可用于本地进程通信(Unix Domain Socket)。
CPU核心与线程关系
-
物理核心与逻辑处理器
- 物理核心数决定实际并行能力。
- 逻辑处理器来自超线程技术,允许一个物理核心模拟多个逻辑核心。
-
Runtime类获取核心数
Runtime.getRuntime().availableProcessors()返回的是逻辑处理器数量。- 不同架构平台(Intel vs AMD/ARM)返回结果可能不同。
上下文切换原理
-
上下文切换定义
- 操作系统保存当前线程状态并加载另一个线程状态的过程。
- 包括寄存器、程序计数器、缓存数据等。
-
上下文切换代价
- 一次上下文切换通常需要约5,000~20,000个时钟周期。
- 相较之下,普通指令执行只需几个到几十个时钟周期。
-
性能调优建议
- 上下文切换频繁会导致性能下降。
- 使用工具如
vmstat、pidstat监控切换频率。
并发与并行区别
-
并行(Parallelism)
- 同一时刻执行多个任务。
- 依赖多核CPU,如两个队伍同时使用两台售货机。
-
并发(Concurrency)
- 一段时间内交替执行多个任务。
- 如一个售货机被两个队伍轮流使用。
- 体现为时间片轮转下的"看似同时"。
Java线程创建方式
-
继承Thread类
- 创建子类并重写
run()方法。 - 实例化后调用
start()启动线程。
- 创建子类并重写
-
实现Runnable接口
- 实现
run()方法,传入Thread构造器。 - 更推荐使用,避免类继承限制。
- 实现
-
Callable + FutureTask
Callable<V>提供返回值,适用于需要结果的任务。FutureTask包装Callable,作为Runnable传入线程。- 通过
future.get()获取异步执行结果。
线程终止方式
-
自然终止
run()方法执行完毕。- 抛出未捕获异常导致提前结束。
-
强制终止(不推荐)
stop()、suspend()、resume()等方法已废弃。- 原因:不会释放资源,易造成死锁、文件损坏等问题。
-
中断机制(推荐)
- 调用
interrupt()设置中断标志位为true。 - 线程主动检查
isInterrupted()或interrupted()状态。 - 阻塞方法(如
sleep()、get())抛出InterruptedException,可响应中断。
- 调用
线程启动与运行区别
-
start()方法- 启动新线程,在JVM中注册并与操作系统线程绑定。
- 只能调用一次,否则抛出
IllegalThreadStateException。
-
run()方法- 普通方法,直接调用仅在当前线程中执行。
- 可重复调用,不影响线程状态。
- 示例:调用
thread.run()将在主线程中执行