"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了

你可能感兴趣的:("unable to create new native thread"--多线程常见问题)