"unable to create new native thread"--多线程常见问题

Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
    at java.lang.Thread.start0(Native Method)
    at java.lang.Thread.start(Thread.java:714)
    at java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:950)
    at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1368)
    at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)
    at com.wenniuwuren.concurrent.newCachedThreadPoolTest.main(newCachedThreadPoolTest.java:15)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

业务开发过程中, 比如说我们要实现一个功能, 但是该功能需要调用其他系统的多个接口做数据组装, 多个被调用接口之间无依赖关系, 按照正常逻辑, 我们可以串行依次调用多个接口, 假如有三个接口, 我们的代码如下:

//伪代码   伪代码  伪代码!  重要的事情说三遍
public Result demo(){
  //调用第一个接口, 耗时1s
  Result  interface1Result = interface1();

 //调用第二个接口, 耗时2s
  Result  interface2Result = interface2();

 //调用第三个接口, 耗时3s
  Result  interface3Result = interface3();

//组装数据
 Result  result = interface1Result + interface2Result +interface3Result;
return result;
}

满足业务需要,没有问题, 当我们考虑性能的时候, 这个接口的最小耗时等于三个接口的耗时之和, 也就是6s, 针对这种情况, 我们想当然的觉得, 可以把串行的接口调用改成并行, 这样的话, 接口响应时间应该提升为3s, 伪代码入下:

ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
//伪代码   伪代码  伪代码!  重要的事情说三遍
public Result demo(){
  //调用第一个接口, 耗时1s
cachedThreadPool.execute(new Runnable() {

@Override
public void run() {
   Result  interface1Result = interface1();
}
});
}

 //调用第二个接口, 耗时2s
cachedThreadPool.execute(new Runnable() {

@Override
public void run() {
  Result  interface2Result = interface2();
}
});
}


 //调用第三个接口, 耗时3s
cachedThreadPool.execute(new Runnable() {

@Override
public void run() {
  Result  interface3Result = interface3();
}
});
}

//组装数据
 Result  result = interface1Result + interface2Result +interface3Result;
return result;
}

这种情况下, 我们使用线程池来并行调用三个接口, 最终响应时间得到了提升, 但是单机的tps在压测的时候, 就会出现我们文章标题的错误, 因为每个方法需要3s的处理时长, 当压力不断增加的时候, 线程池没有可用的线程时, 会一直新建线程, 当超出jvm的栈内存大小时, 就会报出无法再创建线程的错误.

这种情况下, 我们可以使用定长的线程池, 比如估计一个线程池大小, 100, 当无线程可用的时候, 我们就阻塞等待, 这种情况比较稳, 但需要能够比较准确的估算线程池大小, 要不然服务器资源会有一定的浪费, 这不符合我们勤俭持家程序员的风格, 于是下面我们介绍一下, 如何准确的估算线程池的大小:

引自:《Java Concurrency in Practice》即《java并发编程实践》


image.png

如上图,在《Java Concurrency in Practice》一书中,给出了估算线程池大小的公式:

Nthreads=Ncpu*Ucpu*(1+w/c),其中

Ncpu=CPU核心数

Ucpu=cpu使用率,0~1

W/C=等待时间与计算时间的比率

比如说, 服务器cpu为32核, 一般cpu使用到80%会引起系统告警, 等待时间估计为 0.2s, 计算时间为 0.1s,
针对这种情况:

  Nthreads=32*0.8*(1+0.2/0.1) = 76.8

所以我们就设置线程池大小为 75 就ok了

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

推荐阅读更多精彩内容

  • 下面是我自己收集整理的Java线程相关的面试题,可以用它来好好准备面试。 参考文档:-《Java核心技术 卷一》-...
    阿呆变Geek阅读 14,887评论 14 507
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,341评论 11 349
  • 他写半朽的诗 昨日就被埋葬的诗。 难以捉摸 是某个末日,群山如梦跳转不已。 虹霓触电,困在边界的城市 震颤崩解。 ...
    没缸阅读 234评论 0 0
  • 文|张涔汐 她那时候太年轻,不知道所有命运赠送的礼物,早已在暗中标好了价格。——茨威格 壹 昨天一个好友跟我抱怨被...
    张涔汐阅读 906评论 1 11
  • 房间内,琉璃忐忑的看看上官玉清和墨玉桓,定了定神,终于一狠心,眼一闭,嘴一张,就吞下了上官玉清苦心为自己提炼的的“...
    记搏阅读 767评论 0 4