局部变量-线程安全
1. 局部变量是线程安全的
- 局部变量是线程安全的,因为它们存在于每个线程自己的**调用栈(栈帧)**中。
- 没有共享就没有并发问题,这是最本质的线程安全策略之一。
- 线程封闭技术是高效避免并发问题的关键思路之一,广泛应用于数据库连接池等场景。
1.1. 方法执行的流程
示例代码:
// 返回斐波那契数列
int[] fibonacci(int n) {// 创建结果数组int[] r = new int[n];// 初始化第一、第二个数r[0] = r[1] = 1; // ①// 计算 2..nfor(int i = 2; i < n; i++) {r[i] = r[i-2] + r[i-1];}return r;
}
int a = 7;
int[] b = fibonacci(a);
int[] c = b;
当你调用 fibonacci(a) 的时候,CPU 要先找到方法 fibonacci() 的地址,然后跳转到这个地址去执行代码,最后 CPU 执行完方法 fibonacci() 之后,要能够返回。首先找到调用方法的下一条语句的地址:也就是int[] c=b;
的地址,再跳转到这个地址去执行。
方法并不是 CPU 原生支持的概念,实际是编译器把方法翻译成一条条机器指令。
- 通过 CPU 的堆栈寄存器(经常称为调用栈),找到调用方法的参数和返回地址。
调用栈:
- 栈帧和方法是同生共死的。
方法调用流程:
-
- 调用前将参数压栈
- 跳转到方法地址执行
- 执行完后返回,对应的栈帧自动弹出
1.2. 调用栈与线程
- 每个线程有自己独立的调用栈(Call Stack)。
- 每次方法调用,会创建一个栈帧(Stack Frame):
-
- 保存:方法参数、局部变量、返回地址等。
- 方法执行完,栈帧出栈,局部变量随之销毁。
结论:局部变量存在于各线程自己的调用栈中,天然线程隔离,不存在共享,也就没有并发问题。
1.3. 线程封闭
概念:数据仅在单线程内访问,无需加锁也不会出现并发问题。
局部变量的线程封闭:
- 典型例子,天然封闭在栈帧内。
应用案例:
- 数据库连接池中的
Connection
:
-
- 并不是线程安全的对象
- 但连接池通过“每个线程持有一个 Connection,不共享”来实现线程封闭,避免并发问题。
堆vs栈:
类型 | 存储位置 | 生命周期 | 是否线程安全 |
局部变量 | 调用栈 | 方法调用期间 | ✅ 是(线程私有) |
对象(new) | 堆 | 手动控制(GC) | ❌ 否(需加锁或隔离) |
- 堆内对象生命周期更长,可被多个线程共享,必须注意并发访问。
- 栈内局部变量生命周期随方法结束而结束,不存在共享问题。.