Spring Boot多线程共用OutputStream无输出,求复用同一输出流方案
问题分析与解决方案
你的代码存在两个核心问题导致输出流无数据,同时多线程复用OutputStream也需要保证线程安全:
核心问题
- 主线程提前退出,输出流被提前关闭:
StreamingResponseBody的执行线程在启动所有子线程后直接结束,Spring容器会认为响应已完成,提前关闭输出流,子线程后续的write操作无法生效。 - 多线程写流无同步,数据混乱/丢失:多个线程直接写入同一个
OutputStream,会导致字符串截断、内容拼接错乱,即使有输出也会是无效内容。
修复后的代码
public ResponseEntity<StreamingResponseBody> startBombing(Request request) { int numberOfThreads = request.getConfig().getNumberOfThreads() == 0 ? 5 : request.getConfig().getNumberOfThreads(); long requestPerThread = request.getConfig().getRequestPerThread() == 0 ? 100 : request.getConfig().getRequestPerThread(); StreamingResponseBody responseBody = response -> { // 用线程池管理子线程,替代手动创建Thread ExecutorService executor = Executors.newFixedThreadPool(numberOfThreads); // 计数器,等待所有子线程执行完成 CountDownLatch latch = new CountDownLatch(numberOfThreads); // 写流锁,保证多线程写操作的线程安全 Object writeLock = new Object(); for (int i = 1; i <= numberOfThreads; i++) { int finalI = i; executor.submit(() -> { try { for (int j = 1; j <= requestPerThread; j++) { HttpRequest req = createRequest(request.getHttpRequest()); Object res = doRequest(req); String outputContent = String.format("Thread number: %d: call number: %d TimeStamp: %d:::: RESPONSE: %s%n", finalI, j, System.currentTimeMillis(), res); System.out.print(outputContent); // 同步写流操作,避免多线程冲突 synchronized (writeLock) { response.write(outputContent.getBytes()); // 强制刷新流,确保数据及时输出到客户端 response.flush(); } } } catch (Exception e) { // 可选:将异常信息写入输出流,让客户端感知错误 String errorMsg = String.format("Thread %d failed: %s%n", finalI, e.getMessage()); synchronized (writeLock) { response.write(errorMsg.getBytes()); response.flush(); } e.printStackTrace(); } finally { // 每个子线程完成后递减计数器 latch.countDown(); } }); } try { // 等待所有子线程执行完毕,再结束响应 latch.await(); response.flush(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { // 关闭线程池 executor.shutdown(); } }; return ResponseEntity.ok() .contentType(MediaType.TEXT_PLAIN) .body(responseBody); }
关键修改说明
- 线程池与CountDownLatch:用
ExecutorService管理线程更高效,CountDownLatch确保StreamingResponseBody的执行线程等待所有子线程完成后再结束,避免输出流被提前关闭。 - 线程安全写流:通过
synchronized块包裹写操作,保证同一时间只有一个线程写入输出流,避免数据混乱。也可以用new SynchronizedOutputStream(response)替代手动加锁,效果一致。 - 强制刷新流:每次写操作后调用
flush(),确保数据及时发送到客户端,而不是被缓存。 - 格式化输出:用
String.format整理输出内容,增加换行符%n提升可读性。
内容的提问来源于stack exchange,提问作者Prasad Parab
相关产品推荐
相关产品推荐

