java 线程安全解决方案-java线程中有线程
当多个线程访问共享变量时,就会产生并发问题。 Java中的变量都是共享变量吗? 很多同学对所有的局部变量都设置了同步,显然没有把共享变量解释清楚,那么Java局部变量有没有并发问题呢?
很多人都知道局部变量没有竞争。 至于原因? 不能说清楚。
你需要了解一点编译原理。 在 CPU 级别没有方法的概念。 只有个别说明。 编译程序就是将高级语言中的方法转换成一条一条的指令。 你可以站在编译器实现的角度,思考如何完成方法到指令的转换?
方法是如何执行的
在高级语言的普通语句中,翻译成CPU的指令比较简单,但是方法调用比较复杂。 例如下面三行代码: 第1行,声明一个int变量a; 第 2 行,调用方法 fibonacci(a); 第 3 行,将 b 赋值给 c。
int a = 7;
int[] b = fibonacci(a);
int[] c = b;
当你调用fibonacci(a)时,CPU首先要找到fibonacci()方法的地址,然后跳转到这个地址去执行代码,最后CPU必须在执行完fibonacci()方法后才能返回。 先找到下一条语句调用方法的地址,即int[]c=b;的地址,然后跳转到这个地址去执行。 大家可以参考下图加深理解。
至此,方法调用的过程你就清楚了,但是还有一个问题,CPU到哪里去找方法调用的参数和返回地址呢? 如果你熟悉它,你会立刻想到CPU的堆栈寄存器。 CPU支持栈结构,先进后出。 因为这个和方法调用有关,所以叫做调用栈。
比如有A、B、C三个方法java 线程安全解决方案,它们的调用关系是A->B->C(A调用B,B调用C)。 在运行时,将构建以下调用堆栈。 每个方法在调用栈中都有自己独立的空间,称为栈帧,每个栈帧都有相应方法所需的参数和返回地址。 当一个方法被调用时java 线程安全解决方案,一个新的栈帧被创建并压入调用栈; 当方法返回时,相应的栈帧会自动弹出。 换句话说,堆栈框架和方法同生同死。
使用堆栈结构来支持方法调用是如此普遍,以至于在 CPU 中内置了一个堆栈寄存器。 虽然各种编程语言定义的方法千奇百怪,但是方法内部的执行原理却惊人的一致:都是通过栈结构来解决的。 Java语言虽然是由虚拟机解释执行的,但是方法调用也是通过使用栈结构来解决的。
局部变量存储在哪里?
我们已经知道方法调用在CPU眼中是如何执行的,但还有一个关键问题,方法中的局部变量存在于何处?
局部变量的作用域是在方法内部,也就是说当方法执行的时候,局部变量是没有用的。 局部变量应该和方法一起生死,所以把局部变量放在调用栈上是很合理的。 事实上,这是真的。 ,局部变量放在调用栈中,所以调用栈结构变成如下:
大家基本都会知道,所有的教科书都会告诉你堆中存在新的对象,但是很多人不知道堆和栈的区别,为什么要区分堆和栈。 现在清楚了,局部变量与方法同生同死,一个变量要想越过方法的边界,就必须在堆中。
调用栈和线程
两个线程可以同时调用不同参数的同一个方法。 调用栈和线程是什么关系? 答:每个线程都有自己独立的调用栈。 如果不是这种情况,则线程相互干扰,如下图所示:
现在我们来看一开始的问题,Java方法中的局部变量是否存在并发问题? 现在你应该很清楚了,一点问题都没有,因为每个线程都有自己独立的调用栈,局部变量保存在各自调用的线程栈中,不会被共享。 自然不会有共享问题。 再说一遍:不,分享会很痛! !
线程关闭
方法中的局部变量不会与其他线程共享,所以不存在并发问题。 这个想法很好,也成为了解决问题的方法。 这里有个响亮的名字:线程闭包。
也就是说,数据只能在单个线程中访问。 由于没有共享,所以即使不同步也不会有并发问题。
使用线程关闭的情况有很多。 数据库连接池获取的连接,jdbc规范没有要求连接必须是线程安全的。 数据库连接池采用线程关闭技术,获得的连接在释放前不会分配给其他请求。 这确保不会出现并发问题。