两点:
一定要起其他线程处理Process exec = Runtime.getRuntime().exec(cmd); 执行完返回的Process的输出流和输出流。否则会导致主线程卡住
Process的inputStream流,因为一直有访问根本不会为空,readInputStream方法里有写
主方法
public static Boolean runJar() {
try {
//通过接口获取是否需要更新jar包
//拿不到或者报错,都是走之前的jar包
int status = 0;
//拿到,判断下状态是否需要更新
if (0 == status) {
//需要更新,解析出URL
String downLodePath = "downLodePath";
String savePath = "savePath";
String fileName = "fileName";
//下载文件
if (downLoadFromUrl(downLodePath, savePath, fileName)) {
//下载成功,执行 java -jar 方法
String cmd = "java -jar " + savePath + fileName + " &";
logger.info("cmd:{}",cmd);
Process exec = Runtime.getRuntime().exec(cmd);
logger.info("项目运行jar包命令方式启动完成");
InputStream inputStream = exec.getInputStream();
InputStream errStream = exec.getErrorStream();
Thread inputThread = new Thread(new InputStreamThread(inputStream));
inputThread.start();
Thread errThread = new Thread(new InputStreamThread(errStream));
errThread.start();
return true;
}else{
logger.error("jar包下载失败,url:{}",downLodePath);
return false;
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
return false;
}
return false;
}
主方法用到的方法
/**
* 封装的通过URL下载包的方法
*/
private static boolean downLoadFromUrl(String urlStr, String savePath, String fileName) {
final HttpClient httpClient = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_1_1)
.connectTimeout(Duration.ofSeconds(50))
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(urlStr))
.header("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)")
.build();
final CompletableFuture<HttpResponse<Path>> httpResponseCompletableFuture = httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofFile(Path.of(savePath + fileName)));
final HttpResponse<Path> join = httpResponseCompletableFuture.join();
return HttpServletResponse.SC_OK == join.statusCode();
}
/**
* 从输入流中获取字节数组
*/
private static void readInputStream(InputStream inputStream) throws IOException {
byte[] buffer = new byte[1024];
int len = 0;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int i=1;
while((len = inputStream.read(buffer)) != -1) {
/**
* 这里是重点
*
* 因为inputStream流根本不会为空,所以这个while根本不会跳出去,因此ByteArrayOutputStream就会慢慢积累变得很大导致内存泄漏
* 因此这里每500次重置一下
*/
if(i>500){
bos.close();
bos = new ByteArrayOutputStream();
i = 1;
}
bos.write(buffer, 0, len);
logger.info(new String(buffer,0,len));
i++ ;
}
bos.close();
logger.info("while逻辑执行完毕!");
return ;
}
private static class InputStreamThread implements Runnable{
private InputStream inputStream;
public InputStreamThread(InputStream stream){
inputStream = stream;
}
public void run(){
try {
readInputStream(inputStream);
}catch (Exception e){
logger.error(e);
}
}
}
待优化点
1、可以优化一下主方法里的 Process exec = Runtime.getRuntime().exec(cmd); 这块,因为用原生的Process时候,虽然打断点的时候可以去看执行的linux命令执行状态,但是如果直接在主方法里使用会报错。百度了只要重新就行,我用不到,所以没改。可以自行修改。
2、readInputStream 方法可以优化优化,当时为了解决问题就很潦草。