java线程

创建多少线程合适?

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

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

  • I/O密集型:

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

  • 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没有尾递归优化
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 不管你是新程序员还是老手,你一定在面试中遇到过有关线程的问题。Java语言一个重要的特点就是内置了对并发的支持,让...
    尧淳阅读 5,485评论 0 25
  • 下面是Java线程相关的热门面试题,你可以用它来好好准备面试。 1) 什么是线程? 线程是操作系统能够进行运算调度...
    冰箱哥哥阅读 3,430评论 0 2
  • 0 前言 在过去单CPU时代,单任务在一个时间点只能执行单一程序。之后发展到多任务阶段,计算机能在同一时间点并行执...
    七寸知架构阅读 13,312评论 6 95
  • 文/花随裙裾 慕耶溪呆立在原处,准备送出的竞标书模本掉落在地上。多年压抑的思念、不甘、疑问一起涌上心头,内心...
    安妮wang阅读 1,586评论 0 2
  • 文/明至 无限风光在险峰。 人生中的许多选择,都是鱼和熊掌不可兼得,得到一样,意味着放弃其他。正如美国诗人罗伯特·...
    作家明至阅读 3,345评论 0 2