0%

Java线程(下):为什么局部变量是线程安全的

多个线程同时访问共享变量的时候,会导致并发问题。局部变量则不存在数据竞争,不需要设置同步。

在CPU层面,没有方法概念,只有一条条指令。编译程序,负责把高级语言里的方法转换成一条条的指令。

方法是如何被执行的

高级语言里的普通语句,翻译成CPU的指令相对简单,方法的调用比较复杂。

当调用方法的时候,CPU要先找到方法的地址,然后跳转到这个地址去执行代码,最后CPU执行完方法之后,要能够返回。首先找到调用方法的下一条语句的地址,再跳转到这个地址去执行。

CPU通过CPU的堆栈寄存器来找到调用方法的参数和返回地址。CPU支持一种先入后出的栈结构。因为这个栈是和方法调用相关的,因此称为调用栈

程序计数器存放的是下一条指令的地址,CPU的堆栈寄存器存放的是调用方法的参数和返回地址,而调用栈存放的是局部变量。

每个方法在调用栈里都有自己的独立空间,称为栈帧,每个栈帧里都有对应方法需要的参数和返回地址。当调用方法时,会创建新的栈帧,并压入调用栈;当方法返回时,对应的栈帧就会被自动弹出。栈帧和方法是同生共死的

利用栈结构来支持方法调用非常普遍,CPU里内置了栈寄存器。各家编程语言的方法的内部执行原理都是靠栈结构解决的。

局部变量存哪里

局部变量的作用域是方法内部,当方法执行完,局部变量就没用了,局部变量应该和方法同生共死。调用栈的栈帧就是和方法同生共死的,所以局部变量就是放到了调用栈里

new出来的对象是在堆里,局部变量是在栈里。局部变量是和方法同生共死的,一个变量如果想跨越方法的边界,就必须创建在堆里。

new出来的对象放到堆空间中,[句柄在句柄池中(堆区)句柄和对象的生命周期相同],而引用的指针是放在栈中的。

调用栈与线程

两个线程可以同时用不同的参数调用相同的方法。每个线程都有自己独立的调用栈,避免线程相互干扰。

Java方法里面的局部变量不存在并发问题。因为每个线程都有自己的调用栈,局部变量保存在线程各自的调用栈里面,不会共享,所以自然也就没有并发问题。

线程封闭

方法里的局部变量,因为不会和其它线程共享,所以没有并发问题,已经成为解决并发问题的一个重要技术-线程封闭仅在单线程内访问数据,由于不存在共享,所以即便不同步也不会有并发问题,性能也很好。

采用线程封闭技术的案例:从数据库连接池里获取的连接Connection,在JDBC规范里并没有要求这个Connection必须是线程安全的。数据库连接池通过线程封闭技术,保证一个Connection一旦被一个线程获取之后,在这个线程关闭Connection之前的这段时间里,不会再分配给其他线程,从而保证了Connection不会有并发问题。

总结

调用栈是一个通用的计算机概念,所有的编程语言都会涉及到。