基本上我开始多个线程,这又开始了一个应该被监控的长时间运行的进程。在示例中," long"运行进程是简单的ping调用。当进程结束或失败时,IpListener有责任触发事件。
现在出现了一个有趣的部分:每次在控制台输出中找到ip时,都会触发onIpFound方法。我想要做的是等到所有线程第一次找到IP然后继续执行main方法。但是,启动的进程应该继续运行,最终应该触发上述的onProcessEnd和onProcessFailure事件。你有任何提示来实现这种行为(以合理的方式)吗?
以下是小示例应用程序的各个部分,以便您更好地理解:
ReaderTest.java
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");
}
}
答案 0 :(得分:0)
这样做的一种方法是引入一个在IpReader(或你的监听器)线程之间共享的对象,当它们找到ip时它们都可以更新。
例如介绍这个简单的类:
eval
现在在您的主要方法中:
class WaitTask {
private int numberRead;
public int getNumberRead(){
return numberRead;
}
public void increment(){
numberRead++;
}
}
在你的IpThread中添加:
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
}
答案 1 :(得分:0)
使用“原子变量”来存储IP地址。这些可以由ping线程设置并由主线程检查。在主线程中停止“条件变量”。当ping线程收到ip地址时,它们将唤醒主线程。 https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Condition.html https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicReference.html
这样做,你设置一个变量,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();
}