精华内容
下载资源
问答
  • for update 和 for update nowait的相同点对操作的数据行进行加锁,在事务提交前防止其他操作对数据的修改使用for update测试工具 pgadmin,打开SQL窗口,关闭事务的自动提交,改成手动提交事务select * from table1 ...

    for update 和 for update nowait的相同点

    对操作的数据行进行加锁,在事务提交前防止其他操作对数据的修改

    使用for update

    测试工具 pgadmin,打开SQL窗口,关闭事务的自动提交,改成手动提交事务

    select * from table1 where id ='001' for update;

    再用pgadmin打开新的一个SQL窗口,打开事务的自动提交,在执行同样的SQL

    select * from table1 where id ='001' for update;

    会发现第二个窗口的SQL显示正在查询中,不能出来结果,因为在等待第一个窗口的事务的提交或者回滚

    使用for update nowait

    测试工具 pgadmin,打开SQL窗口,关闭事务的自动提交,改成手动提交事务

    select * from table1 where id ='001' for update nowait;

    再用pgadmin打开新的一个SQL窗口,打开事务的自动提交,在执行同样的SQL

    select * from table1 where id ='001' for update nowait;

    此时第二个窗口的SQL会报错:

    ERROR: could not obtain lock on row in relation "table1"

    ********** 错误 **********

    ERROR: could not obtain lock on row in relation "table1"

    SQL 状态: 55P03

    因为窗口1的SQL事务没提交或者回滚,窗口2的SQL由于不能获取行级锁,报错提示。

    业务运用:可以使用在后台防抖防止前台瞬间的连续点击操作,后台在代码中捕获该异常,进行提示即可。

    展开全文
  • 接着来分析缓冲区,哪来的这个东西,当Runtime对象调用exec(cmd),JVM会启动一个子进程,该进程会与JVM进程建立三个管道连接:标准输入,标准输出和标准错误流。假设该程序不断在向标准输出流和标准错误流写数据,...

    问题的关键在缓冲区这个地方:可执行程序的标准输出比较多,而运行窗口的标准缓冲区不够大,所以发生阻塞。接着来分析缓冲区,哪来的这个东西,当Runtime对象调用exec(cmd)后,JVM会启动一个子进程,该进程会与JVM进程建立三个管道连接:标准输入,标准输出和标准错误流。假设该程序不断在向标准输出流和标准错误流写数据,而JVM不读取的话,当缓冲区满之后将无法继续写入数据,最终造成阻塞在waitfor()这里。知道问题所在,我们解决问题就好办了。查看网上说的方法多数是开两个线程在waitfor()命令之前读出窗口的标准输出缓冲区和标准错误流的内容。

    转自http://yearsaaaa123789.iteye.com/blog/1404865

    在编写Java程序时,有时候我们需要调用其他的诸如exe,shell这样的程序或脚本。在Java中提供了两种方法来启动其他程序:(1)

    使用Runtime的exec()方法(2) 使用ProcessBuilder的start()方法 。Runtime和ProcessBulider提供了不同的方式来启动程序,设置启动参数、环境变量和工作目录。但是这两种方法都会返回一个用于管理操作系统进程的Process对象。这个对象中的waitFor()是我们今天要讨论的重点。

    来说说我遇到的实际情况:我想调用ffmpeg程序来对一首歌曲进行转码,把高音质版本的歌曲转为多种低码率的文件。但是在转码完成之后需要做以下操作:读取文件大小,写入ID3信息等。这时我们就想等转码操作完成之后我们可以知道。

    如下这样代码

    Java代码  9bded883a59038026a7713c1e24c6985.png

    Process p =null;

    try{

    p = Runtime.getRuntime().exec("notepad.exe");

    } catch(Exception e) {

    e.printStackTrace();

    }

    System.out.println("我想被打印...");

    在notepad.exe被执行的同时,打印也发生了,但是我们想要的是任务完成之后它才被打印。

    之后发现在Process类中有一个waitFor()方法可以实现。如下:

    Java代码9bded883a59038026a7713c1e24c6985.png

    Process p =null;

    try{

    p = Runtime.getRuntime().exec("notepad.exe");

    p.waitFor();

    } catch(Exception e) {

    e.printStackTrace();

    }

    System.out.println("我想被打印...");

    这下又出现了这样的现象,必须要等我们把记事本关闭打印语句才会被执行。并且你不碰手动关闭它那程序就一直不动,程序貌似挂了....

    46ba8008e77a7a0cf259d3a7732a041a.gif.这是什么情况,想调用个别的程序有这么难吗?让我们来看看waitFor()的说明:

    JDK帮助文档上这么说:如有必要,一直要等到由该 Process 对象表示的进程已经终止。如果已终止该子进程,此方法立即返回。但是直接调用这个方法会导致当前线程阻塞,直到退出子进程。对此JDK文档上还有如此解释:因为本地的系统对标准输入和输出所提供的缓冲池有效,所以错误的对标准输出快速的写入何从标准输入快速的读入都有可能造成子进程的所,甚至死锁。好了,问题的关键在缓冲区这个地方:可执行程序的标准输出比较多,而运行窗口的标准缓冲区不够大,所以发生阻塞。接着来分析缓冲区,哪来的这个东西,当Runtime对象调用exec(cmd)后,JVM会启动一个子进程,该进程会与JVM进程建立三个管道连接:标准输入,标准输出和标准错误流。假设该程序不断在向标准输出流和标准错误流写数据,而JVM不读取的话,当缓冲区满之后将无法继续写入数据,最终造成阻塞在waitfor()这里。知道问题所在,我们解决问题就好办了。查看网上说的方法多数是开两个线程在waitfor()命令之前读出窗口的标准输出缓冲区和标准错误流的内容。代码如下:

    Java代码9bded883a59038026a7713c1e24c6985.png

    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,newFile("C:\\ffmpeg-git-670229e-win32-static\\bin"));

    //获取进程的标准输入流

    finalInputStream is1 = p.getInputStream();

    //获取进城的错误流

    finalInputStream is2 = p.getErrorStream();

    //启动两个线程,一个线程负责读标准输出流,另一个负责读标准错误流

    newThread() {

    publicvoidrun() {

    BufferedReader br1 = newBufferedReader(newInputStreamReader(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();

    newThread() {

    publicvoidrun() {

    BufferedReader br2 = newBufferedReader(newInputStreamReader(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()方法阻塞无法返回的问题。但是在其中过程中我却发现真正起关键作用的缓冲区是getErrorStream()说对应的那个缓冲区没有被清空,意思就是说其实只要及时读取标准错误流缓冲区的数据程序就不会被block。

    Java代码  9bded883a59038026a7713c1e24c6985.png

    StringBuffer sb =newStringBuffer();

    try{

    Process pro = Runtime.getRuntime().exec(cmdString);

    BufferedReader br = newBufferedReader(newInputStreamReader(pro.getInputStream()),4096);

    String line = null;

    inti =0;

    while((line = br.readLine()) !=null) {

    if(0!= i)

    sb.append("\r\n");

    i++;

    sb.append(line);

    }

    } catch(Exception e) {

    sb.append(e.getMessage());

    }

    returnsb.toString();

    不过这种写法不知道是不是适合所有的情况,网上其他人说的需要开两个线程可能不是没有道理。这个还是具体问题具体对待吧。

    到这里问题的原因也清楚了,问题也被解决了,是不是就结束了。让我们回过头来再分析一下,问题的关键是处在输入流缓冲区那个地方,子进程的产生的输出流没有被JVM及时的读取最后缓冲区满了就卡住了。如果我们能够不让子进程向输入流写入数据,是不是可以解决这个问题。对于这个想法直接去ffmpeg官网查找,最终发现真的可以关闭子进程向窗口写入数据。命令如下:

    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这句

    这才是我们想要的结果:

    Java代码  9bded883a59038026a7713c1e24c6985.png

    try{

    p = Runtime.getRuntime().exec("cmd /c ffmpeg -loglevel quiet -i     D:\\a.mp3 -ab 168k -ar 22050 -acodec libmp3lame D:\\b.mp3",null,

    newFile("C:\\ffmpeg-git-670229e-win32-static\\bin"));

    p.waitFor();

    } catch(Exception e) {

    e.printStackTrace();

    }

    System.out.println("我想被打印...");

    最后是自己写的一个简单的操作MP3文件的类

    Java代码  9bded883a59038026a7713c1e24c6985.png

    packagecom.yearsaaaa.util;

    importjava.io.File;

    importjava.io.FileInputStream;

    importjava.math.BigDecimal;

    importjavazoom.jl.decoder.Bitstream;

    importjavazoom.jl.decoder.Header;

    /**

    * @className:MP3Util.java

    * @classDescription:

    * @author:MChen

    * @createTime:2012-2-9

    */

    publicclassMP3Util {

    /**

    * 获取文件大小,以M为单位,保留小数点两位

    */

    publicstaticdoublegetMP3Size(String path)

    {

    File file = newFile(path);

    doublesize = (double)file.length()/(1024*1024);

    size = newBigDecimal(size).setScale(2,BigDecimal.ROUND_UP).doubleValue();

    System.out.println("MP3文件的大小为:"+size);

    returnsize;

    }

    /**

    * 该方法只能获取mp3格式的歌曲长度

    * 库地址:http://www.javazoom.net/javalayer/javalayer.html

    */

    publicstaticString getMP3Time(String path)

    {

    String songTime = null;

    FileInputStream fis = null;

    Bitstream bt = null;

    File file = newFile(path);

    try{

    fis = newFileInputStream(file);

    intb=fis.available();

    bt=newBitstream(fis);

    Header h=bt.readFrame();

    inttime=(int) h.total_ms(b);

    inti=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();

    }

    }

    returnsongTime;

    }

    /**

    * 将源MP3向下转码成低品质的文件

    * @参数: @param srcPath 源地址

    * @参数: @param bitrate 比特率

    * @参数: @param desfile 目标文件

    * @return void

    * @throws

    */

    publicstaticvoidmp3Transcoding(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,newFile("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){}

    }

    }

    publicstaticvoidmain(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 = newFile(s);

    mp3Transcoding(f.getAbsolutePath(),"64","d:\\chenmiao.mp3");

    }

    }

    }

    save_snippets.png

    展开全文
  • java Process的waitFor()

    2021-02-26 19:10:50
    这个对象中的waitFor()是我们今天要讨论的重点。 来说说我遇到的实际情况:我想调用ffmpeg程序来对一首歌曲进行转码,把高音质版本的歌曲转为多种低码率的文件。但是在转码完成之后需要做以下操作:读取文件大小,...

    在编写Java程序时,有时候我们需要调用其他的诸如exe,shell这样的程序或脚本。在Java中提供了两种方法来启动其他程序:

    (1) 使用Runtime的exec()方法

    (2) 使用ProcessBuilder的start()方法

    Runtime和ProcessBulider提供了不同的方式来启动程序,设置启动参数、环境变量和工作目录。但是这两种方法都会返回一个用于管理操作系统进程的Process对象。这个对象中的waitFor()是我们今天要讨论的重点。

    来说说我遇到的实际情况:我想调用ffmpeg程序来对一首歌曲进行转码,把高音质版本的歌曲转为多种低码率的文件。但是在转码完成之后需要做以下操作:读取文件大小,写入ID3信息等。这时我们就想等转码操作完成之后我们可以知道。

    如下这样代码

    Java代码   9bded883a59038026a7713c1e24c6985.png

    Process p = null;

    try {

    p = Runtime.getRuntime().exec("notepad.exe");

    } catch (Exception e) {

    e.printStackTrace();

    }

    System.out.println("我想被打印...");

    在notepad.exe被执行的同时,打印也发生了,但是我们想要的是任务完成之后它才被打印。

    之后发现在Process类中有一个waitFor()方法可以实现。如下:

    Java代码   9bded883a59038026a7713c1e24c6985.png

    Process p = null;

    try {

    p = Runtime.getRuntime().exec("notepad.exe");

    p.waitFor();

    } catch (Exception e) {

    e.printStackTrace();

    }

    System.out.println("我想被打印...");

    这下又出现了这样的现象,必须要等我们把记事本关闭, 打印语句才会被执行。并且你不能手动关闭它那程序就一直不动,程序貌似挂了....

    46ba8008e77a7a0cf259d3a7732a041a.gif.这是什么情况,想调用个别的程序有这么难吗?让我们来看看waitFor()的说明:

    JDK帮助文档上这么说:如有必要,一直要等到由该 Process 对象表示的进程已经终止。如果已终止该子进程,此方法立即返回。但是直接调用这个方法会导致当前线程阻塞,直到退出子进程。对此JDK文档上还有如此解释:因为本地的系统对标准输入和输出所提供的缓冲池有效,所以错误的对标准输出快速的写入何从标准输入快速的读入都有可能造成子进程的所,甚至死锁。好了,

    问题的关键在缓冲区这个地方:可执行程序的标准输出比较多,而运行窗口的标准缓冲区不够大,所以发生阻塞。

    接着来分析缓冲区,哪来的这个东西,当Runtime对象调用exec(cmd)后,JVM会启动一个子进程,该进程会与JVM进程建立三个管道连接:标准输入,标准输出和标准错误流。

    假设该程序不断在向标准输出流和标准错误流写数据,而JVM不读取的话,当缓冲区满之后将无法继续写入数据,最终造成阻塞在waitfor()这里。 知道问题所在,我们解决问题就好办了。查看网上说的方法多数是开两个线程在waitfor()命令之前读出窗口的标准输出缓冲区和标准错误流的内容。代码如下:

    Java代码   9bded883a59038026a7713c1e24c6985.png

    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()方法阻塞无法返回的问题。但是在其中过程中我却发现真正起关键作用的缓冲区是getErrorStream()所对应的那个缓冲区没有被清空,意思就是说其实只要及时读取标准错误流缓冲区的数据程序就不会被block。

    Java代码   9bded883a59038026a7713c1e24c6985.png

    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();

    不过这种写法不知道是不是适合所有的情况,网上其他人说的需要开两个线程可能不是没有道理。这个还是具体问题具体对待吧。

    到这里问题的原因也清楚了,问题也被解决了,是不是就结束了。让我们回过头来再分析一下,问题的关键是处在输入流缓冲区那个地方,子进程的产生的输出流没有被JVM及时的读取最后缓冲区满了就卡住了。如果我们能够不让子进程向输入流写入数据,是不是可以解决这个问题。对于这个想法直接去ffmpeg官网查找,最终发现真的可以关闭子进程向窗口写入数据。命令如下:

    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这句

    这才是我们想要的结果:

    Java代码   9bded883a59038026a7713c1e24c6985.png

    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文件的类

    Java代码   9bded883a59038026a7713c1e24c6985.png

    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");

    }

    }

    }

    展开全文
  • JDK对waitFor方法的解释是: public abstract int waitFor() throws InterruptedException 导致当前线程等待,如有必要,一直要等到由该 Process 对象表示的进程已经终止。如果已终止该子进程,此方法立即返回。...

    在编写Java程序时,有时候我们需要调用其他的诸如exe,shell这样的程序或脚本。

    Java虚拟机执行Runtime.getRuntime().exec()方法的过程是:首先克隆一个和当前虚拟机拥有一样环境变量的进程,再用这个进程去执行外部命令,最后再退出这个进程。如果频繁执行这个操作,系统消耗会很大,不仅是CPU,内存负担也很大。

    在Java中提供了两种方法来启动其他程序:

    使用Runtime的exec()方法

    使用ProcessBuilder的start()方法 。

    Runtime和ProcessBulider提供了不同的方式来启动程序,设置启动参数、环境变量和工作目录。但是这两种方法都会返回一个用于管理操作系统进程的Process对象,再调用Process.waitFor()来等待命令执行结束,获取执行结果。

    然而这样简单的调用也是有坑的,有几个地方需要小心留意

    ###1、命令拼接空格问题(推荐使用ProcessBuilder) ####1.1 通过Runtime.getRuntime().exec()方式

    String htmlName = CONTRACT_PATH + File.separator + cid + HTML_POSTFIX;

    String pdfName = CONTRACT_PATH + File.separator + cid + PDF_POSTFIX;

    String command = wkhtmltopdf + " " + htmlName + " " + pdfName;

    Process proc = Runtime.getRuntime().exec(command);

    这种方式下,可以通过字符串直接拼接命令,命令中可以包含有空格。

    ####1.2 通过ProcessBuilder(command).start()方式

    让我们先来看一下ProcessBuilder类的方法,这里传入的command为可变参数类型

    public ProcessBuilder(String... command) {

    this.command = new ArrayList<>(command.length);

    for (String arg : command)

    this.command.add(arg);

    }

    当通过这种方式调用的时候,必须注意,不能将命令拼接成字符串传入,需要将各个参数分别传入,如:

    ProcessBuilder pb = new ProcessBuilder(wkhtmltopdf, htmlName, pdfName);

    Process process = pb.start();

    如果在这种方式下,你传的command是经过拼接的字符串命令(一般情况下是含有空格的),会报错:

    java.io.IOException: Cannot run program "C:/getapkinfo/aapt.exe d": CreateProcess error=2, ϵͳÕҲ»µ½ָ¶

    ###2、调用系统命令的阻塞问题 假如现在我需要在java中调用本地系统的wkhtmltopdf,将html文档转化为pdf文档,并对pdf文档做其他处理。那么在对pdf文档做其他处理之前,我必须要等系统命令对html转pdf的完成,这里就存在一个阻塞的问题。

    JDK对waitFor方法的解释是:

    public abstract int waitFor() throws InterruptedException

    导致当前线程等待,如有必要,一直要等到由该 Process 对象表示的进程已经终止。如果已终止该子进程,此方法立即返回。如果没有终止该子进程,调用的线程将被阻塞,直到退出子进程。

    返回: 进程的出口值。根据惯例,0 表示正常终止。

    抛出: InterruptedException - 如果当前线程在等待时被另一线程中断,则停止等待,抛出 InterruptedException。

    因为本地的系统对标准输入和输出所提供的缓冲池有限,所以错误的对标准输出快速的写入和从标准输入快速的读入都有可能造成子进程死锁。问题的关键在缓冲区这个地方:可执行程序的标准输出比较多,而运行窗口的标准缓冲区不够大,所以发生阻塞。接着来分析缓冲区,当Runtime对象调用exec(cmd)后,JVM会启动一个子进程,该进程会与JVM进程建立三个管道连接:标准输入,标准输出和标准错误流。假设该程序不断在向标准输出流和标准错误流写数据,而JVM不读取的话,当缓冲区满之后将无法继续写入数据,最终造成阻塞在waitfor()这里。

    需要注意读取程序的stdout和stderr都是阻塞的操作,这意味着必须在两个线程里分别读取,而不是在一个线程里一次读取,否则还是有可能出现阻塞的情况:比如先读取stdout再读取stderr,如果程序的stderr输出已经填满了缓冲区,程序就会阻塞不继续执行,但是java线程又阻塞在读取stdout上,只有stdout结束了才会去读取stderr。结果就是互相等待着的过程中哦给你程序卡死了。 为了不让程序变得过于复杂,我们可以把程序的stderr重定向到stdout中,这样只要读取stdout一个就好了。缺点就是没有区分出错的输出信息和正常的输出信息。

    ** 但是在其中过程中真正起关键作用的缓冲区是getErrorStream()对应的那个缓冲区没有被清空,意思就是说其实只要及时读取标准错误流缓冲区的数据程序就不会被block。**

    这里提供一个系统调用的工具类,代码如下:

    import org.apache.commons.io.IOUtils;

    import java.io.IOException;

    import java.io.InputStream;

    import java.util.concurrent.TimeoutException;

    /**

    * 一个进程调用工具.

    * Created by chenyh on 2016/8/5.

    */

    public class ProcessUtils {

    /**

    * 运行一个外部命令,返回状态.若超过指定的超时时间,抛出TimeoutException

    *

    */

    public static ProcessStatus execute(final long timeout, final String... command)

    throws IOException, InterruptedException, TimeoutException {

    ProcessBuilder pb = new ProcessBuilder(command);

    pb.redirectErrorStream(true);

    Process process = pb.start();

    Worker worker = new Worker(process);

    worker.start();

    ProcessStatus ps = worker.getProcessStatus();

    try {

    worker.join(timeout);

    if (ps.exitCode == ProcessStatus.CODE_STARTED) {

    // not finished

    worker.interrupt();

    throw new TimeoutException();

    } else {

    return ps;

    }

    } catch (InterruptedException e) {

    // canceled by other thread.

    worker.interrupt();

    throw e;

    } finally {

    process.destroy();

    }

    }

    private static class Worker extends Thread {

    private final Process process;

    private ProcessStatus ps;

    private Worker(Process process) {

    this.process = process;

    this.ps = new ProcessStatus();

    }

    public void run() {

    try {

    InputStream is = process.getInputStream();

    try {

    ps.output = IOUtils.toString(is);

    } catch (IOException ignore) { }

    ps.exitCode = process.waitFor();

    } catch (InterruptedException e) {

    Thread.currentThread().interrupt();

    }

    }

    public ProcessStatus getProcessStatus() {

    return this.ps;

    }

    }

    public static class ProcessStatus {

    public static final int CODE_STARTED = -257;

    public volatile int exitCode;

    public volatile String output;

    }

    }

    Ref:

    展开全文
  • 这个对象中的waitFor()是我们今天要讨论的重点。 来说说我遇到的实际情况:我想调用ffmpeg程序来对一首歌曲进行转码,把高音质版本的歌曲转为多种低码率的文件。但是在转码完成之后需要做以下操作:读取文件大小,...
  • OS error code 107: Transport endpoint is not connected 操作系统错误代码107:运输端点没有连接上 OS error code 108: Cannot send after transport endpoint shutdown 操作系统错误代码108:运输终点关闭无法...
  • – MRP0一直显示WAIT_FOR_GAP 现象: MRP0一直显示WAIT_FOR_GAP的状态。 有了上次的教训,这次看到MRP0一直是WAIT_FOR_GAP的状态,先看看StandbyREDO是否正确构建了,答案是完全没问题。 下来确认各种信息: ...
  • 在编写Java程序时,有时候我们需要调用其他的诸如exe,shell这样的程序或脚本。在Java中提供了两种方法来启动其他程序: (1) 使用Runtime的exec()方法 ...这个对象中的waitFor()是我们今天要讨...
  • 打印两三张没问题,打印多了程序就出现这种卡死的情况,...二、查找原因经过查找资料了解到:Process.waitFor可能导致死锁?因为本地的系统对标准输入和输出所提供的缓冲池有限,所以错误的对标准输出快速的写入和从...
  • Mac 系统下使用java Runtime导出 mysql 数据库,以及Process.waitFor()的返回值的含义发布时间:2018-12-14作者:laosun阅读(1400)Mac 系统下使用java java.lang.Runtime.getRuntime().exec 导出 mysql 数据库,以及...
  • 【IT168技术】前些天使用Java调用外部程序的时候,发现线程会堵塞在waitfor()方法。调用方法如下:Process process=...为什么会堵塞呢,原因是当调用exec(cmd),JVM会启动一个子进程,该进程会与JVM进程建立...
  • 在正常情况下我们可以用Project.waitfor()的返回值是否等于0的方法来判断java调用外部程序是Pass或者是Fail。但是这个方法往往会被因进程堵塞而导致程序发生死锁,无法再继续执行外部程序。因为本地的系统对标准输入...
  • 点击关注公众号,利用碎片时间学习写在开头,大概 4 年前,听到运维同学提到 TIME_WAIT 状态的 TCP 连接过多的问题,但是当时没有去细琢磨;最近又听人说起,是一个新手进行压测过程...
  • Java中在阻塞调用系统命令的时候,一般是使用Runtime.getRuntime().exec(command)返回一个process对象,再调用Process.waitFor()来等待命令执行结束,获取执行结果。然而这样简单的调用也是有坑的,有几个地方需要...
  • [mysqld] wait_timeout=31536000 interactive_timeout=31536000 在mysqld下面添加以上两行,后面的数字是时间 首先服务中找到mysql,然后右键属性,在可执行文件的路径中,使劲向拖动鼠标就可以看到my.ini的文件了...
  • 进入系统,打开终端,输入下面命令, cd /etc/systemd/system/network-online.target.wants/ 查看该文件夹,该文件夹下有三个文件,systemd-networkd-wait-online.service、networking.service、NetworkManager-...
  • 这时候子进程不断向主进程发生数据,而主进程调用Process.waitfor后已挂起。当前子进程和主进程之间的缓冲区塞满,子进程不能继续写数据,然后也会挂起。 5. 这样子进程等待主进程读取数据,主进程等待子进程结束...
  • 背景介绍11g数据库在进行IMPDP数据恢复的时候,有时候恢复操作一直卡这不动,这时候可以查询下该恢复会话的等待事件,如果看到等待事件是“wait for unread message on broadcast channel”,通过以下思路解决。...
  • 本文,我们来讲解下 Java 并发中的基础的基础,核心的核心,...接着,我们将开发一个简单的应用程序,并在合格应用程序里处理并发问题,以方便大家理解和巩固wait()和notify()。Java 中的线程同步 ( Thread Synchr...
  • .waitFor()卡死

    2021-10-14 08:48:06
    经过查找资料了解到:Process.waitFor可能导致死锁? 因为本地的系统对标准输入和输出所提供的缓冲池有限,所以错误的对标准输出快速的写入和从标准输入快速的读入都有可能造成子进程死锁。问题的关键在缓冲区这个...
  • 现象: 虚拟机安装ubuntu20.04 TLS系统,开机总会卡在等待网络连接好长时间。 如图: 解决办法: ...查看该文件夹,该文件夹下有个文件,systemd-networkd-wait-online.service vi systemd-n...
  • 原标题:面试官:为什么 wait() 方法需要写在while里,而不是if?译者:scugxl来源:http://www.importnew.com/26584.html问:为什么是 while 而不是 if ?大多数人都知道常见的使用 synchronized 代码:synchronized...
  • sh-4.1# netstat -an |awk '/tcp/ {++S[$NF]}END {for (a in S) print a , S[a]}'TIME_WAIT41CLOSE_WAIT1ESTABLISHED2LISTEN7TCP/IP TIME_WAIT状态原理:通信双方建立TCP连接,主动关闭连接的一方就会进入TIME_WAIT...
  • oracle中UPDATE nowait 的使用方法介绍1、UPDATE nowait 应用以下场景:查询某条数据,并对其开启数据库事务。如果查询的当前数据没有加锁,则正确返回结果,并对当前数据加锁,如果查询的当前数据已在事务中,已...
  • 问题起因6 e+ L# s; Y; ~7 w3 T8 Y3 k# W! W4 G8 b) k7 S- J# I前段时间调研nacos,用来代替zookeeper当作dubbo的注册中心,使用的是nacos的1.1.4版本。...但这与本文无关,后面会专门写一篇文章...
  • I am just having hard time to understand concept behind putting wait() in object class For this questions sake consider as if wait() and notifyAll() are in thread class在Java语言中,对一个对象的特定...
  • 问题描述:在不考虑系统负载、CPU、内存等情况下,netstat监控大量ESTABLISHED连接与Time...# netstat -n | awk '/^tcp/ {++y[$NF]} END {for(w in y) print w, y[w]}'CLOSE_WAIT 348ESTABLISHED 1240TIME_WAIT 5621...
  • 问题: 项目日志报wait_timeout超时,报错如下 引起原因:com.mysql.cj.jdbc.exceptions.CommunicationsException:通信链接失败 ...
  • 首先,我是一个初学Android的世界,所以请道歉,如果这是愚蠢的问题..我正在尝试做以下事情:启用移动数据等待10秒一个。...我无法使waitfor(int seconds)功能。我尝试使用Runnable PostDelayed方法,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 293,121
精华内容 117,248
关键字:

for后面接什么wait