waitFor()一直等待,线程阻塞问题

本文介绍了如何使用Java中的Runtime类和Process类来调用外部程序,并提供了多种常见情况下的解决方案,包括执行DOS命令、打开文件及处理标准输出等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

waitFor()一直等待,线程阻塞问题 —转载–Java中使用Runtime和Process类运行外部程序

使用Runtime.getRuntime().exec()方法可以在java程序里运行外部程序。
Java代码 收藏代码

1. exec(String command)  
2. exec(String command, String envp[], File dir)  
3. exec(String cmd, String envp[])  
4. exec(String cmdarray[])  
5. exec(String cmdarray[], String envp[])  
6. exec(String cmdarray[], String envp[], File dir)  

一般的应用程序可以直接使用第一版本,当有环境变量传递的时候使用后面的版本。其中2和6版本可以传递一个目录,标识当前目录,因为有些程序是使用相对目录的,所以就要使用这个版本。

cmd.exe /c start
使用DOS命令(比如dir)时也要使用到调用。如果想与调用的程序进行交互,那么就要使用该方法的返回对象Process了,通过Process的getInputStream(),getOutputStream()和getErrorStream()方法可以得到输入输出流,然后通过InputStream可以得到程序对控制台的输出信息,通过OutputStream可以给程序输入指令,这样就达到了程序的交换功能。
用Java编写应用时,有时需要在程序中调用另一个现成的可执行程序或系统命令,这时可以通过组合使用Java提供的Runtime类和Process类的方法实现。下面是一种比较典型的程序模式:
Java代码 收藏代码

1    
2  Process process = Runtime.getRuntime().exec(".\\p.exe");  
3  process.waitfor();  
4    

在上面的程序中,第一行的“.\p.exe”是要执行的程序名,Runtime.getRuntime()返回当前应用程序的Runtime对象,该对象的exec()方法指示Java虚拟机创建一个子进程执行指定的可执行程序,并返回与该子进程对应的Process对象实例。通过Process可以控制该子进程的执行或获取该子进程的信息。第二条语句的目的等待子进程完成再往下执行。
但在windows平台上,如果处理不当,有时并不能得到预期的结果。下面是笔者在实际编程中总结的几种需要注意的情况:
1、执行DOS的内部命令
如果要执行一条DOS内部命令,有两种方法。一种方法是把命令解释器包含在exec()的参数中。例如,执行dir命令,在NT上,可写成exec(“cmd.exe /c dir”),在windows95/98下,可写成“command.exe /c dir”,其中参数“/c”表示命令执行后关闭DOS立即关闭窗口。另一种方法是,把内部命令放在一个批命令my_dir.bat文件中,在Java程序中写成exec(“my_dir.bat”)。如果仅仅写成exec(“dir”),Java虚拟机则会报运行时错误。前一种方法要保证程序的可移植性,需要在程序中读取运行的操作系统平台,以调用不同的命令解释器。后一种方法则不需要做更多的处理。
2、打开一个不可执行的文件
打开一个不可执行的文件,但该文件存在关联的应用程序,则可以有两种方式。以打开一个word文档a.doc文件为例,Java中可以有以下两种写法:
Java代码 收藏代码

1  exec("start .\\a.doc");  
2  exec("Files\\Microsoft Office\\office\\winword.exe .\\a.doc");  

显然,前一种方法更为简捷方便。
3、执行一个有标准输出的DOS可执行程序
在Windows平台上,运行被调用程序的DOS窗口在程序执行完毕后往往并不会自动关闭,从而导致Java应用程序阻塞在waitfor()语句。导致该现象的一个可能的原因是,该可执行程序的标准输出比较多,而运行窗口的标准输出缓冲区不够大。解决的办法是,利用Java中Process类提供的方法让Java虚拟机截获被调用程序的DOS运行窗口的标准输出,在waitfor()命令之前读出窗口的标准输出缓冲区中的内容。一段典型的程序如下:
Java代码 收藏代码

1    
2  String s;  
3  Process process = Runtime.getRuntime().exec("cmd /c dir \\windows");  
4  BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream());  
5  while((s=bufferedReader.readLine()) != null)  
6  System.out.println(s);  
7  process.waitfor();  
### Java 线程阻塞与唤醒的方法 在多线程编程中,线程阻塞和唤醒是实现并发控制的重要机制。Java 提供了多种方法来实现线程阻塞和唤醒,主要包括 `wait()`、`notify()`、`notifyAll()`、`sleep()`、`join()` 以及 `LockSupport` 类等。 #### 阻塞线程的方法 1. **`wait()`**:该方法使当前线程进入等待状态,并释放持有的对象锁。线程会一直等待,直到其他线程调用 `notify()` 或 `notifyAll()` 方法唤醒它。需要注意的是,`wait()` 必须在同步代码块或同步方法中调用,否则会抛出 `IllegalMonitorStateException`。 2. **`sleep(long millis)`**:该方法使当前线程暂停执行指定时间(以毫秒为单位),但不会释放任何锁资源。线程在睡眠结束后会自动恢复运行。 3. **`join()`**:该方法用于等待另一个线程执行完成后再继续执行当前线程。例如,线程 A 调用 `threadB.join()`,A 将被阻塞,直到线程 B 执行完毕。 4. **`LockSupport.park()`**:该方法通过 `LockSupport` 类提供的静态方法实现线程阻塞操作。它可以替代传统的 `wait()` 和 `notify()`,并且不需要持有对象锁即可使用。 以下是一个简单的示例,展示如何使用 `wait()` 和 `notify()`: ```java class WaitNotifyExample { private boolean flag = false; public void waitForFlag() throws InterruptedException { synchronized (this) { while (!flag) { wait(); // 线程进入等待状态 } System.out.println("Flag is true, continuing execution"); } } public void setFlag() { synchronized (this) { flag = true; notify(); // 唤醒等待线程 } } public static void main(String[] args) { WaitNotifyExample example = new WaitNotifyExample(); Thread waiter = new Thread(() -> { try { example.waitForFlag(); } catch (InterruptedException e) { e.printStackTrace(); } }); Thread notifier = new Thread(() -> { try { Thread.sleep(2000); // 模拟延迟 example.setFlag(); } catch (InterruptedException e) { e.printStackTrace(); } }); waiter.start(); notifier.start(); } } ``` #### 唤醒线程的方法 1. **`notify()`**:唤醒一个正在等待该对象监视器的线程。如果有多个线程等待,则选择其中一个线程唤醒,具体选择哪个线程由 JVM 决定。 2. **`notifyAll()`**:唤醒所有正在等待该对象监视器的线程。这些线程会在锁释放后竞争重新获取锁并继续执行。 3. **`interrupt()`**:中断线程可以唤醒因调用 `sleep()` 或 `join()` 而阻塞线程,并抛出 `InterruptedException`。这种方式适用于需要提前终止线程的情况。 4. **`LockSupport.unpark(Thread thread)`**:该方法通过 `LockSupport` 类提供的静态方法实现对指定线程的唤醒操作。它可以在任意时刻调用,即使目标线程尚未调用 `park()`,也不会导致异常。 以下是一个使用 `LockSupport` 的示例: ```java import java.util.concurrent.locks.LockSupport; public class LockSupportExample { public static void main(String[] args) { Thread mainThread = Thread.currentThread(); Thread counterThread = new Thread(() -> { for (int i = 1; i <= 1000; i++) { System.out.println(i); if (i == 500) { LockSupport.unpark(mainThread); // 唤醒主线程 } } }); counterThread.start(); LockSupport.park(); // 主线程进入阻塞状态 System.out.println("Main thread was unparked."); } } ``` #### 特殊情况:IO 阻塞的处理 对于因 I/O 操作而阻塞线程,Java 的中断机制无法直接唤醒它们。此时需要依赖具体的 I/O 操作提供的机制,例如设置超时时间、检查操作状态等[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值