欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java?Process中waitFor()的問題詳解

 更新時間:2022年12月20日 08:52:58   作者:yearsaaaa123789  
這篇文章主要給大家介紹了關于Java?Process中waitFor()問題的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下

在編寫Java程序時,有時候我們需要調用其他的諸如exe,shell這樣的程序或腳本。在Java中提供了兩種方法來啟動其他程序: (1) 使用Runtime的exec()方法 (2) 使用ProcessBuilder的start()方法 。Runtime和ProcessBulider提供了不同的方式來啟動程序,設置啟動參數、環(huán)境變量和工作目錄。但是這兩種方法都會返回一個用于管理操作系統進程的Process對象。這個對象中的waitFor()是我們今天要討論的重點。

來說說我遇到的實際情況:我想調用ffmpeg程序來對一首歌曲進行轉碼,把高音質版本的歌曲轉為多種低碼率的文件。但是在轉碼完成之后需要做以下操作:讀取文件大小,寫入ID3信息等。這時我們就想等轉碼操作完成之后我們可以知道。

如下這樣代碼

Process p = null;
try {
	p = Runtime.getRuntime().exec("notepad.exe");
} catch (Exception e) {
	e.printStackTrace();
}
System.out.println("我想被打印...");

 在notepad.exe被執(zhí)行的同時,打印也發(fā)生了,但是我們想要的是任務完成之后它才被打印。

之后發(fā)現在Process類中有一個waitFor()方法可以實現。如下:

Process p = null;
try {
	p = Runtime.getRuntime().exec("notepad.exe");
	p.waitFor();
} catch (Exception e) {
	e.printStackTrace();
}
System.out.println("我想被打印...");

這下又出現了這樣的現象,必須要等我們把記事本關閉打印語句才會被執(zhí)行。并且你不碰手動關閉它那程序就一直不動,程序貌似掛了.....這是什么情況,想調用個別的程序有這么難嗎?讓我們來看看waitFor()的說明:

JDK幫助文檔上這么說:如有必要,一直要等到由該 Process 對象表示的進程已經終止。如果已終止該子進程,此方法立即返回。但是直接調用這個方法會導致當前線程阻塞,直到退出子進程。對此JDK文檔上還有如此解釋:因為本地的系統對標準輸入和輸出所提供的緩沖池有效,所以錯誤的對標準輸出快速的寫入何從標準輸入快速的讀入都有可能造成子進程的所,甚至死鎖。好了,問題的關鍵在緩沖區(qū)這個地方:可執(zhí)行程序的標準輸出比較多,而運行窗口的標準緩沖區(qū)不夠大,所以發(fā)生阻塞。接著來分析緩沖區(qū),哪來的這個東西,當Runtime對象調用exec(cmd)后,JVM會啟動一個子進程,該進程會與JVM進程建立三個管道連接:標準輸入,標準輸出和標準錯誤流。假設該程序不斷在向標準輸出流和標準錯誤流寫數據,而JVM不讀取的話,當緩沖區(qū)滿之后將無法繼續(xù)寫入數據,最終造成阻塞在waitfor()這里。 知道問題所在,我們解決問題就好辦了。查看網上說的方法多數是開兩個線程在waitfor()命令之前讀出窗口的標準輸出緩沖區(qū)和標準錯誤流的內容。代碼如下:

Runtime rt = Runtime.getRuntime();
String command = "cmd /c ffmpeg -loglevel quiet -i "+srcpath+" -ab "+bitrate+"k -acodec libmp3lame "+desfile;
try {
 p = rt.exec(command ,null,new File("C:\\ffmpeg-git-670229e-win32-static\\bin"));
 //獲取進程的標準輸入流
 final InputStream is1 = p.getInputStream(); 
 //獲取進城的錯誤流
 final InputStream is2 = p.getErrorStream();
 //啟動兩個線程,一個線程負責讀標準輸出流,另一個負責讀標準錯誤流
 new Thread() {
    public void run() {
       BufferedReader br1 = new BufferedReader(new InputStreamReader(is1));
        try {
            String line1 = null;
            while ((line1 = br1.readLine()) != null) {
                  if (line1 != null){}
              }
        } catch (IOException e) {
             e.printStackTrace();
        }
        finally{
             try {
               is1.close();
             } catch (IOException e) {
                e.printStackTrace();
            }
          }
        }
     }.start();
                              
   new Thread() { 
      public void  run() { 
       BufferedReader br2 = new  BufferedReader(new  InputStreamReader(is2)); 
          try { 
             String line2 = null ; 
             while ((line2 = br2.readLine()) !=  null ) { 
                  if (line2 != null){}
             } 
           } catch (IOException e) { 
                 e.printStackTrace();
           } 
          finally{
             try {
                 is2.close();
             } catch (IOException e) {
                 e.printStackTrace();
             }
           }
        } 
      }.start();  
                              
      p.waitFor();
      p.destroy(); 
     System.out.println("我想被打印...");
    } catch (Exception e) {
            try{
                p.getErrorStream().close();
                p.getInputStream().close();
                p.getOutputStream().close();
                }
             catch(Exception ee){}
          }
   }

 這個方法確實可以解決調用waitFor()方法阻塞無法返回的問題。但是在其中過程中我卻發(fā)現真正起關鍵作用的緩沖區(qū)是getErrorStream()說對應的那個緩沖區(qū)沒有被清空,意思就是說其實只要及時讀取標準錯誤流緩沖區(qū)的數據程序就不會被block。

StringBuffer sb = new StringBuffer();
try {
Process pro = Runtime.getRuntime().exec(cmdString);
BufferedReader br = new BufferedReader(new InputStreamReader(pro.getInputStream()), 4096);
String line = null;
int i = 0;
while ((line = br.readLine()) != null) {
if (0 != i)
sb.append("\r\n");
i++;
sb.append(line);
}
} catch (Exception e) {
sb.append(e.getMessage());
}
return sb.toString();

不過這種寫法不知道是不是適合所有的情況,網上其他人說的需要開兩個線程可能不是沒有道理。這個還是具體問題具體對待吧。

到這里問題的原因也清楚了,問題也被解決了,是不是就結束了。讓我們回過頭來再分析一下,問題的關鍵是處在輸入流緩沖區(qū)那個地方,子進程的產生的輸出流沒有被JVM及時的讀取最后緩沖區(qū)滿了就卡住了。如果我們能夠不讓子進程向輸入流寫入數據,是不是可以解決這個問題。對于這個想法直接去ffmpeg官網查找,最終發(fā)現真的可以關閉子進程向窗口寫入數據。命令如下:

ffmpeg.exe -loglevel quiet -i 1.mp3 -ab 16k -ar 22050 -acodec libmp3lame r.mp3

稍微分析一下:-acodec 音頻流編碼方式 -ab 音頻流碼率(默認是同源文件碼率,也需要視codec而定) -ar 音頻流采樣率(大多數情況下使用44100和48000,分別對應PAL制式和NTSC制式,根據需要選擇),重點就是-loglevel quiet這句 

http://www.ffmpeg.com.cn/index.php/Ffmpeg%E9%80%89%E9%A1%B9%E8%AF%A6%E8%A7%A3

這才是我們想要的結果:

try {
  p = Runtime.getRuntime().exec("cmd /c ffmpeg -loglevel quiet -i     D:\\a.mp3 -ab 168k -ar 22050 -acodec libmp3lame D:\\b.mp3",null,
					new File( "C:\\ffmpeg-git-670229e-win32-static\\bin"));
  p.waitFor();
} catch (Exception e) {
	e.printStackTrace();
}
System.out.println("我想被打印...");

最后是自己寫的一個簡單的操作MP3文件的類

package com.yearsaaaa.util;

import java.io.File;
import java.io.FileInputStream;
import java.math.BigDecimal;

import javazoom.jl.decoder.Bitstream;
import javazoom.jl.decoder.Header;

/**
 * @className:MP3Util.java
 * @classDescription:
 * @author:MChen
 * @createTime:2012-2-9
 */
public class MP3Util {
	
	/**
	 * 獲取文件大小,以M為單位,保留小數點兩位
	 */
	public static double getMP3Size(String path)
	{
		File file = new File(path);
		double size = (double)file.length()/(1024*1024);
		size = new BigDecimal(size).setScale(2,BigDecimal.ROUND_UP).doubleValue();
		System.out.println("MP3文件的大小為:"+size);
		return size;
	}
	
	/**
	 * 該方法只能獲取mp3格式的歌曲長度
	 * 庫地址:http://www.javazoom.net/javalayer/javalayer.html
	 */
	public static String getMP3Time(String path)
	{
		String songTime = null;
		FileInputStream fis = null;
		Bitstream bt = null;
		File file = new File(path);
		try {
			fis = new FileInputStream(file);
			int b=fis.available();
			bt=new Bitstream(fis);
			Header h=bt.readFrame();
			int time=(int) h.total_ms(b);
			int i=time/1000;
			bt.close();
			fis.close();
			if(i%60 == 0)
				songTime = (i/60+":"+i%60+"0");
			if(i%60 <10)
				songTime = (i/60+":"+"0"+i%60);
			else
				songTime = (i/60+":"+i%60);
			System.out.println("該歌曲的長度為:"+songTime);
		}
		catch (Exception e) {
			try {
				bt.close();
				fis.close();
			} catch (Exception ee) {
				ee.printStackTrace();
			}
		}
		return songTime;
	}
	
	/**
	 * 將源MP3向下轉碼成低品質的文件
	 * @參數: @param srcPath 源地址
	 * @參數: @param bitrate 比特率
	 * @參數: @param desfile 目標文件
	 * @return void   
	 * @throws
	 */
	public static void mp3Transcoding(String srcPath,String bitrate,String desFile)
	{	
		//Java調用CMD命令時,不能有空格
		String srcpath = srcPath.replace(" ", "\" \"");
		String desfile = desFile.replace(" ", "\" \"");
		Runtime rt = Runtime.getRuntime();
		String command = "cmd /c ffmpeg -loglevel quiet -i "+srcpath+" -ab "+bitrate+"k -acodec libmp3lame "+desfile;
		System.out.println(command);
		Process p = null;
		try{
			//在Linux下調用是其他寫法
			p = rt.exec(command ,null,new File("C:\\ffmpeg-git-670229e-win32-static\\bin"));
			p.waitFor();
			System.out.println("線程返回,轉碼后的文件大小為:"+desFile.length()+",現在可以做其他操作了,比如重新寫入ID3信息。");
		}
		catch(Exception e){
			e.printStackTrace();
			try{
			    p.getErrorStream().close();
				p.getInputStream().close();
				p.getOutputStream().close();
				}
			catch(Exception ee){}
		}
	}
	
	public static void main(String[] args) {
		//String[] str = {"E:\\Kugou\\陳慧嫻 - 不羈戀人.mp3","E:\\Kugou\\三寸天堂.mp3","E:\\Tmp\\陳淑樺 - 夢醒時分.mp3","E:\\Tmp\\1.mp3","E:\\Test1\\走天涯、老貓 - 楊望.acc","E:\\Test1\\因為愛情 鈴.mp3"};
		String[] str = {"E:\\Kugou\\三寸天堂.mp3"};
		for(String s : str)
		{
			//getMP3Size(s);
			//getMP3Time(s);
			File f = new File(s);
			mp3Transcoding(f.getAbsolutePath(),"64","d:\\chenmiao.mp3");
		}
	}
}

總結 

到此這篇關于Java Process中waitFor()問題的文章就介紹到這了,更多相關Java Process中waitFor()問題內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Java設置token有效期的5個應用場景(雙token實現)

    Java設置token有效期的5個應用場景(雙token實現)

    Token最常見的應用場景之一就是身份驗證,本文主要介紹了Java設置token有效期的5個應用場景(雙token實現),具有一定的參考價值,感興趣的可以來了解一下
    2024-04-04
  • 如何獲得spring上下文的方法總結

    如何獲得spring上下文的方法總結

    這篇文章主要介紹了如何獲得spring上下文的方法總結,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-04-04
  • Java使用自定義注解實現為事件源綁定事件監(jiān)聽器操作示例

    Java使用自定義注解實現為事件源綁定事件監(jiān)聽器操作示例

    這篇文章主要介紹了Java使用自定義注解實現為事件源綁定事件監(jiān)聽器操作,結合實例形式分析了java自定義注解、注解處理、事件監(jiān)聽與響應等相關操作技巧,需要的朋友可以參考下
    2019-10-10
  • SpringBoot整合MyCat實現讀寫分離的方法

    SpringBoot整合MyCat實現讀寫分離的方法

    這篇文章主要介紹了SpringBoot整合MyCat實現讀寫分離的方法,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-04-04
  • Spring cloud restTemplate 傳遞復雜參數的方式(多個對象)

    Spring cloud restTemplate 傳遞復雜參數的方式(多個對象)

    這篇文章主要介紹了Spring cloud restTemplate 傳遞復雜參數的方式(多個對象),需要的朋友可以參考下
    2018-05-05
  • SpringBoot整合MyBatis和MyBatis-Plus請求后不打印sql日志的問題解決

    SpringBoot整合MyBatis和MyBatis-Plus請求后不打印sql日志的問題解決

    本文主要介紹了SpringBoot整合MyBatis和MyBatis-Plus請求后不打印sql日志的問題解決文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2024-07-07
  • SpringBoot統一功能處理示例詳解(攔截器)

    SpringBoot統一功能處理示例詳解(攔截器)

    這篇文章主要介紹了SpringBoot統一功能處理(攔截器),本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-08-08
  • Struts之logic標簽庫詳解

    Struts之logic標簽庫詳解

    本文就Struts之logic標簽庫進行詳細介紹,需要的朋友可以參考下
    2012-11-11
  • Spring?Boot?使用觀察者模式實現實時庫存管理的步驟

    Spring?Boot?使用觀察者模式實現實時庫存管理的步驟

    在現代軟件開發(fā)中,實時數據處理非常關鍵,本文提供了一個使用SpringBoot和觀察者模式開發(fā)實時庫存管理系統的詳細教程,步驟包括創(chuàng)建項目、定義實體類、實現觀察者模式、集成Spring框架、創(chuàng)建RESTful?API端點和測試應用等,這將有助于開發(fā)者構建能夠即時響應庫存變化的系統
    2024-09-09
  • Java實現簡單學生管理系統

    Java實現簡單學生管理系統

    這篇文章主要為大家詳細介紹了Java實現簡單學生管理系統,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-07-07

最新評論