1. 明确一个问题,线程池使用完需要调用shutdown关闭
参考: https://www.jianshu.com/p/6e3f593c2616
2. 不合适的shutdown引发了问题
2.1 背景
某存储项目,给客户提供sdk,其中提供了一个视频上传方法MultiPartOperatorClient,可以将本地视频上传到ceph的对象存储中去。
MultiPartOperatorClient的构造函数同时初始化了VideoClient(用来与存储中台通信)和UrlUploader(根据存储中台生成的url将视频上传到ceph)。ceph的对象存储使用的是Amazon S3兼容方法,因为视频都比较大,使用分片上传,将视频切割为5M的片段,上传之后再合并。分片上传使用了线程池,即UrlUploader里面有线程池,当视频片段都上传完成,会调用线程池的shutdown方法。
代码示例:
/**
* MultiPartOperationClient是提供给客户使用的视频上传方法,屏蔽了分片上传和中台通信等过程
*/
public class MultiPartOperationClient {
private final UrlUploader urlUploader;
private final VideoClient videoClient;
public MultiPartOperatorClient() {
this.videoClient = new VideoClient(accessKey, secretKey);
this.urlUploader = new UrlUploader();
}
...
}
/**
* VideoClient负责与中台通信,根据用户信息获取视频上传地址等
*/
public class VideoClient {
...
}
/**
* VideoClient负责与中台通信,根据用户信息获取视频上传地址等
*/
public class UrlUploader {
ExecutorService executeService = new ThreadPoolExecutor(线程池的参数省略);
public void multiPartupload() {
while() {
...
Future<String> result = this.executeService.submit(() -> 上传视频片段的方法);
...
}
this.executeService.shutdown();
}
...
}
2.2 产生了一个问题
在使用sdk的过程中,如果初始化了一个MultiPartOperationClient后,理应可以使用这个MultiPartOperationClient不停地上传视频,甚至多线程调用MultiPartOperationClient同时上传多个视频。
但是MultiPartOperationClient和UrlUploader是两个同事分别写的。UrlUploader的作者为了保证自己的代码内存不泄露,在UrlUploader.multiPartupload()方法最后调用了shutdown()方法关闭了线程池,即当前视频的所有片段都上传完成后关闭线程池。
MultiPartOperationClient的作者没注意到这个问题,将UrlUploader的初始化放在了构造函数里,这样MultiPartOperationClient如果想上传多个视频,每次上传视频使用的都是同一个UrlUploader,但是第一个视频上传完成后UrlUploader的线程池已经关闭,无法再接收视频片段上传任务,这时就会报错:java.util.concurrent.RejectedExecutionException
。
java.util.concurrent.RejectedExecutionException
的意思是线程数超过了当前线程池容量,有两种可能:
- 提交太多线程超过了线程池容量
- 线程池被关闭容量为0自然提交任何线程都是超过了容量。
2.3 解决办法
- UrlUploader的初始化不要放在MultiPartOperationClient的构造函数中,每次调用时构造(这样有点浪费资源?)。
- UrlUploader作为MultiPartOperationClient的私有方法,在MultiPartOperationClient的finalize()方法中执行线程池的shutdown()方法。
网友评论