ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

局部变量为何天生安全:从调用栈看 Java 并发编程的线程封闭

局部变量为何天生安全:从调用栈看 Java 并发编程的线程封闭 问题背景在并发编程中多线程同时访问共享变量往往会导致数据竞争和并发问题。但并非所有变量都会成为共享变量。比如下面这个计算斐波那契数列的方法当多个线程同时调用时方法内部的数组r是否存在并发风险int[] fibonacci(int n) { // 创建结果数组 int[] r new int[n]; // 初始化第一、第二个数 r[0] r[1] 1; // ① // 计算2..n for(int i 2; i n; i) { r[i] r[i-2] r[i-1]; } return r; }很多开发者都知道局部变量是线程安全的但未必清楚背后的原理。本文从 JVM 和编译原理的角度梳理局部变量安全的根本原因。原理分析方法在 CPU 层面的执行过程在 CPU 层面并不存在方法的概念只有一条条指令。编译器负责将高级语言中的方法转换为指令序列。以以下三行代码为例int a 7 int[] b fibonacci(a); int[] c b;当调用fibonacci(a)时CPU 需要先找到该方法的地址并跳转执行执行完后还需返回调用处的下一条指令地址即int[] c b的地址。这个调用过程可以用下图理解这里就产生了一个关键问题CPU 去哪里寻找调用方法的参数和返回地址答案是 CPU 内置的堆栈寄存器它支持一种栈结构——调用栈。假设有 A、B、C 三个方法调用关系为 A - B - C运行时就会构建出对应的调用栈。每个方法在调用栈中都有自己的独立空间称为栈帧栈帧中保存了该方法所需的参数和返回地址。调用方法时创建新的栈帧并压入调用栈方法返回时对应栈帧自动弹出两者同生共死。局部变量存在哪里局部变量的作用域是方法内部方法执行完毕局部变量即失效这与栈帧的生命周期完全一致因此局部变量被保存在栈帧中。在 Java 中new出来的对象存放在堆中而局部变量存放在栈中。从生命周期角度很容易理解局部变量与方法同生共死而一个变量若想跨越方法的边界就必须创建在堆中。调用栈与线程的关系多个线程可以用不同参数调用同一个方法它们之间的调用栈互不干扰。每个线程都有自己独立的调用栈否则线程之间会互相干扰。回到最初的问题Java 方法内部的局部变量是否存在并发问题答案是否定的。每个线程拥有独立的调用栈局部变量保存在各自的调用栈中不会被共享自然不会产生并发问题。线程封闭通过局部变量的分析可以得出一个核心思路没有共享就没有伤害。这一思路发展成了并发编程中的重要技术——线程封闭即仅在单线程内访问数据由于不存在共享并发问题自然无从谈起。总结方法调用的过程找到方法位置执行并返回结果。CPU 层面没有方法概念只有指令。每次方法调用创建一个栈帧栈帧包含局部变量和参数生命周期与方法一致。每个线程有独立的调用栈栈帧中的局部变量不会跨线程共享。常见问题问局部变量为什么不会被多个线程共享答每个线程都有自己独立的调用栈局部变量保存在线程各自的调用栈中不会共享。问线程封闭的核心思想是什么答仅在单线程内访问数据由于不存在共享所以便不会出现并发问题。
返回列表