在等待多个线程的事件同时保持启动的进程运行之后执行

时间:2015-12-07 21:10:59

标签: java multithreading

基本上我开始多个线程,这又开始了一个应该被监控的长时间运行的进程。在示例中," 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");
    }

}

2 个答案:

答案 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();
}