okhttp3:无法创建新的本机线程

okhttp3 : unable to create new native thread

遇到一个奇怪的问题,试了几次都无法重现。如何重现它以及它为什么会发生?

非并发任务,循环调用HTTP连接

    public static Response syncGet(URL url) throws IOException {
        log.debug("http url:{}", url);
        OkHttpClient client = new OkHttpClient().newBuilder().connectTimeout(15L, TimeUnit.SECONDS)
                .readTimeout(30L, TimeUnit.SECONDS).build();

        Request request = new Request.Builder().url(url).get().addHeader("cache-control", "no-cache").build();
        Call call = client.newCall(request);
        return call.execute();
    }

堆栈:


    2019-12-05 11:20:56.068 ERROR 18484 --- [   scheduling-1] o.s.s.s.TaskUtils$LoggingErrorHandler    : Unexpected error occurred in scheduled task

    java.lang.OutOfMemoryError: unable to create new native thread
        at java.lang.Thread.start0(Native Method) [na:1.8.0_181]
        at java.lang.Thread.start(Thread.java:717) [na:1.8.0_181]
        at java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:957) [na:1.8.0_181]
        at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1378) [na:1.8.0_181]
        at okhttp3.ConnectionPool.put(ConnectionPool.java:153) ~[okhttp-3.12.1.jar:na]
        at okhttp3.OkHttpClient.put(OkHttpClient.java:167) ~[okhttp-3.12.1.jar:na]
        at okhttp3.internal.connection.StreamAllocation.findConnection(StreamAllocation.java:266) ~[okhttp-3.12.1.jar:na]
        at okhttp3.internal.connection.StreamAllocation.findHealthyConnection(StreamAllocation.java:135) ~[okhttp-3.12.1.jar:na]
        at okhttp3.internal.connection.StreamAllocation.newStream(StreamAllocation.java:114) ~[okhttp-3.12.1.jar:na]
        at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:42) ~[okhttp-3.12.1.jar:na]
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147) ~[okhttp-3.12.1.jar:na]
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121) ~[okhttp-3.12.1.jar:na]
        at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93) ~[okhttp-3.12.1.jar:na]
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147) ~[okhttp-3.12.1.jar:na]
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121) ~[okhttp-3.12.1.jar:na]
        at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93) ~[okhttp-3.12.1.jar:na]
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147) ~[okhttp-3.12.1.jar:na]
        at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:126) ~[okhttp-3.12.1.jar:na]
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147) ~[okhttp-3.12.1.jar:na]
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121) ~[okhttp-3.12.1.jar:na]
        at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:254) ~[okhttp-3.12.1.jar:na]
        at okhttp3.RealCall.execute(RealCall.java:92) ~[okhttp-3.12.1.jar:na]
        at chances.cms.util.HttpUtil.syncGet(HttpUtil.java:67) ~[classes/:na]
        at chances.cms.task.CheckTask.checkContent(CheckTask.java:76) ~[classes/:na]
        at chances.cms.task.CheckTask.check(CheckTask.java:60) ~[classes/:na]
        at chances.cms.task.CheckTask.check(CheckTask.java:51) ~[classes/:na]
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_181]
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_181]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_181]
        at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_181]
        at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:84) ~[spring-context-5.2.1.RELEASE.jar:5.2.1.RELEASE]
        at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) ~[spring-context-5.2.1.RELEASE.jar:5.2.1.RELEASE]
        at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:93) [spring-context-5.2.1.RELEASE.jar:5.2.1.RELEASE]
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [na:1.8.0_181]
        at java.util.concurrent.FutureTask.run(FutureTask.java:266) [na:1.8.0_181]
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access1(ScheduledThreadPoolExecutor.java:180) [na:1.8.0_181]
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) [na:1.8.0_181]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_181]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_181]
        at java.lang.Thread.run(Thread.java:748) [na:1.8.0_181]

OkHttpClients should be shared

OkHttp performs best when you create a single OkHttpClient instance and reuse it for all of your HTTP calls. This is because each client holds its own connection pool and thread pools. Reusing connections and threads reduces latency and saves memory.

more detail: https://square.github.io/okhttp/3.x/okhttp/okhttp3/OkHttpClient.html

因此在 syncGet 方法之外创建 client 应该可以解决您的问题。