java线程

2019-10-18  本文已影响0人  Easy的幸福
创建多少线程合适?

多线程本质上是提升多核CPU的利用率,所以根据使用情况分为:计算密集型、io密集型。

一个通用公式(仅供参考):

最佳线程数=1 +(I/O耗时 / CPU耗时)

最佳线程数=CPU核数 * [ 1 +(I/O耗时 / CPU耗时)]

这个只能做为参考,真正的项目中要依赖压测,重点关注CPU、I/O设备的利用率和性能指标(响应时间、吞吐量)之间的关系(IO耗时是可以通过apm 工具来计算)。

如果说是集群部署,那么线程数就根据自己的机器的核心参数来设置。

方法是如何被执行的

具体示例:

int a = 7;
int[] b = fibonacci(a);
int[] c = b;

''''''''

int[] fibonacci(int n) {
  // 创建结果数组
  int[] r = new int[n];
 
  return r;
}
当你调用fibonacci(a)的时候,CPU要先找到方法 fibonacci() 的地址,然后跳转到这个地址去执行代码,最后CPU执行完方法 fibonacci() 之后,要能够返回。首先找到调用方法的下一条语句的地址:也就是int[] c=b;的地址,再跳转到这个地址去执行。 image

CPU通过CPU的堆栈寄存器找到调用方法的参数和返回地址.

有三个方法A、B、C,他们的调用关系是A->B->C(A调用B,B调用C),在运行时,会构建出下面这样的调用栈。每个方法在调用栈里都有自己的独立空间,称为栈帧,每个栈帧里都有对应方法需要的参数和返回地址。当调用方法时,会创建新的栈帧,并压入调用栈;当方法返回时,对应的栈帧就会被自动弹出。也就是说,栈帧和方法是同生共死的。 image
每个线程都有自己独立的调用栈
image

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

这个思路很好,已经成为解决并发问题的一个重要技术,同时还有个响当当的名字叫做线程封闭,比较官方的解释是:仅在单线程内访问数据

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

递归调用太深,为什么可能导致栈溢出?

因为每调用一个方法就会在栈上创建一个栈帧,方法调用结束后就会弹出该栈帧,而栈的大小不是无限的,所以递归调用次数过多的话就会导致栈溢出。而递归调用的特点是每递归一次,就要创建一个新的栈帧,而且还要保留之前的环境(栈帧),直到遇到结束条件。所以递归调用一定要明确好结束条件,不要出现死循环,而且要避免栈太深。
解决方法:

  1. 简单粗暴,不要使用递归,使用循环替代。缺点:代码逻辑不够清晰;
  2. 限制递归次数;
  3. 使用尾递归,尾递归是指在方法返回时只调用自己本身,且不能包含表达式。编译器或解释器会把尾递归做优化,使递归方法不论调用多少次,都只占用一个栈帧,所以不会出现栈溢出。然鹅,Java没有尾递归优化
上一篇下一篇

猜你喜欢

热点阅读