Java 等待多线程事件后执行,同时保持已启动进程运行
基本上,我启动了多个线程,这些线程再次启动了一个应该被监控的长时间运行的进程。在本例中,“长时间”运行的进程是简单的ping调用。IpListener有责任在进程结束或失败时触发事件 现在是有趣的部分:每当在控制台输出中发现ip时,都会触发onIpFound方法。我想做的是等待所有线程第一次找到IP,然后继续执行main方法。但是,启动的进程应该继续运行,并最终触发前面提到的onProcessEnd和onProcessFailure事件。您是否有任何提示来实现此行为(以合理的方式) 以下是小示例应用程序的各个部分,以帮助您更好地理解: ReaderTest.javaJava 等待多线程事件后执行,同时保持已启动进程运行,java,multithreading,Java,Multithreading,基本上,我启动了多个线程,这些线程再次启动了一个应该被监控的长时间运行的进程。在本例中,“长时间”运行的进程是简单的ping调用。IpListener有责任在进程结束或失败时触发事件 现在是有趣的部分:每当在控制台输出中发现ip时,都会触发onIpFound方法。我想做的是等待所有线程第一次找到IP,然后继续执行main方法。但是,启动的进程应该继续运行,并最终触发前面提到的onProcessEnd和onProcessFailure事件。您是否有任何提示来实现此行为(以合理的方式) 以下是小示例
package com.test;
public class ReaderTest {
public static void main(String[] args) {
IpReader ipReader = new IpReader("stackoverflow.com");
ipReader.setListener(new SimpleIpListener());
new Thread(ipReader).start();
IpReader ipReader2 = new IpReader("stackexchange.com");
ipReader2.setListener(new SimpleIpListener());
new Thread(ipReader2).start();
// TODO: start next step as soon as both ips are known
}
}
IpReader.java
package com.test;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class IpReader implements Runnable {
private IpListener ipListener = null;
private String hostName;
public IpReader(String hostName) {
this.hostName = hostName;
}
public interface IpListener {
public void onIpFound(String ip);
public void onProcessEnd(String string);
public void onProcessFailure(String string);
}
public void setListener(IpListener ipListener) {
this.ipListener = ipListener;
}
@Override
public void run() {
// TODO Auto-generated method stub
ProcessBuilder pb = new ProcessBuilder().command("cmd", "/c", "ping", "-n", "10", hostName);
pb.redirectErrorStream(true);
Process process = null;
try {
try {
process = pb.start();
String line = null;
// print stream
BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((line = br.readLine()) != null) {
Pattern p = Pattern.compile("(?:[0-9]{1,3}\\.){3}[0-9]{1,3}");
Matcher m = p.matcher(line);
if (m.find()) {
// IPv4 found
if (ipListener != null) {
ipListener.onIpFound(m.group(0));
}
}
}
process.waitFor();
if (ipListener != null)
ipListener.onProcessEnd("Process END");
} finally {
if (process != null)
process.destroy();
}
} catch (InterruptedException | IOException e) {
if (ipListener != null)
ipListener.onProcessFailure("Process failure");
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
SimpleIpListener.java
package com.test;
import com.test.IpReader.IpListener;
public class SimpleIpListener implements IpListener {
private boolean ipFound = false;
@Override
public void onIpFound(String ip) {
if (!ipFound)
System.out.println("IP " + ip + " found.");
ipFound = true;
}
@Override
public void onProcessEnd(String string) {
System.out.println("Process ended.");
}
@Override
public void onProcessFailure(String string) {
System.out.println("Process failure");
}
}
一种方法是引入一个在IpReader(或您的侦听器)线程之间共享的对象,当它们找到ip时,都可以更新该对象 例如,介绍这个简单的类:
class WaitTask {
private int numberRead;
public int getNumberRead(){
return numberRead;
}
public void increment(){
numberRead++;
}
}
现在在您的主要方法中:
public static void main(String[] args) {
// create an instance of that class, which keeps track of how many threads found an ip
final WaitTask waitTask = new WaitTask();
// create an int to know how many we are expecting
final int totalThreadsRunning = 2;
IpReader ipReader = new IpReader("stackoverflow.com");
ipReader.setListener(new SimpleIpListener());
// pass our object to the thread
ipReader.setWaitTask(waitTask);
new Thread(ipReader).start();
IpReader ipReader2 = new IpReader("stackexchange.com");
ipReader2.setListener(new SimpleIpListener());
// pass our object to the thread
ipReader2.setWaitTask(waitTask);
new Thread(ipReader2).start();
// Now wait until the wait task object is safe to be accessed
synchronized(waitTask){
// once we know it's safe let's check if we read as many responses as required
while(waitTask.getNumberRead() < totalThreadsRunning )
{
// instead of looping forever wait for a thread to notify that
// the wait task number read was changed
waitTask.wait();
// the wait blocks execution and waits until waitTask was
// changed, after that the while loop condition gets evaluated
// once we read as many as required we exit the loop
}
}
// when we reach here all threads have finished so do anything you want
}
使用“原子变量”存储ip地址。这些可以由ping线程设置,并由主线程检查。在主线程中,在“条件变量”上暂停。当ping线程收到ip地址时,它们将唤醒主线程。
这将要做的是,设置一个变量,ping线程可以发布数据,主线程也可以读取。此外,主线程将在条件变量上停止(并且不消耗处理器时间),但在发现新ip时唤醒。然后它将检查两个ip引用是否都已填充
在全球范围内定义这些:
AtomicReference ip1 = new AtomicReference(new String(""));
AtomicReference ip2 = new AtomicReference(new String(""));
final Lock lock = new ReentrantLock();
final Condition wakeup = lock.newCondition();
然后在主线程中
while(ip1.get().length == 0 && ip2.get().length == 0)
{
wakeup.await();
}
在IpReader定义中:
private String hostName;
private AtomicReference ipstring;
public IpReader(String hostName,AtomicReference outputstring) {
this.hostName = hostName;
this.ipstring = outputstring;
}
最后
@Override
public void onIpFound(String ip, AtomicReference ref) {
if (!ipFound)
System.out.println("IP " + ip + " found.");
ipFound = true;
ref.set(ip);
wakeup.signal();
}
@Override
public void onIpFound(String ip, AtomicReference ref) {
if (!ipFound)
System.out.println("IP " + ip + " found.");
ipFound = true;
ref.set(ip);
wakeup.signal();
}