多线程ArrayList迭代

时间:2014-11-10 07:32:28

标签: java multithreading arraylist javafx-8

电贺!

我正在用Java编写一个简单的服务器监控应用程序(JavaFX8)。当前的实现能够逐个ping目标机器,并将它们绘制到JavaFX LineChart上。每台机器都是一个“Target”对象,它保存在ArrayList(Observable)中。我的问题是“一个接一个”的部分。 ping目标的代码是一个返回ping的Callable。我不知何故需要多线程处理这个过程,这样我就可以一次ping至少四个目标。过去的尝试导致了诸如四个线程同时ping同一目标的怪癖,导致非常无意义且处理器密集的冗余。继续我目前的循环...

public void beginPing() {
    ExecutorService exec = Executors.newCachedThreadPool();

    Runnable r = new Runnable() {
        @Override
        public void run() {
            while (true) {
                for (Target t : targets) {
                    String ping = null;
                    if (t.flagsProperty().get().contains("A")) {
                        try {
                            Callable c = new Pinger(t);
                            ping = c.call().toString();
                            switch (ping) {
                                case "TIME_OUT":
                                    for (XYChart.Series s : lineChart.getData()) {
                                        if (s.getName().equals(t.nameProperty().get())) {
                                            addToChart(s, cycle, 00.00);
                                        }
                                    }
                                    t.setStatus("TIME OUT");
                                    t.setLastrtt("TIME_OUT");
                                    t.setTimeouts(t.timeoutsProperty().get() + 1);
                                    logUtil.log(LogUtil.INFO, t.nameProperty().get() + " - timed out!");
                                    break;
                                case "UNKNOWN_HOST":
                                    t.setStatus("ERROR");
                                    t.setLastrtt("UNKNOWN HOST");
                                    logUtil.log(LogUtil.WARNING, t.nameProperty().get() + " - unknown host!");
                                    break;
                                case "UNREACHABLE":
                                    t.setStatus("ERROR");
                                    t.setLastrtt("UNREACHABLE HOST");
                                    logUtil.log(LogUtil.WARNING, t.nameProperty().get() + " - is unreachable!");
                                    break;
                                default:
                                    t.setLastrtt(ping);
                                    t.setStatus("ACTIVE");
                                    for (XYChart.Series s : lineChart.getData()) {
                                        if (s.getName().equals(t.nameProperty().get())) {
                                            addToChart(s, cycle, Double.valueOf(ping));
                                        }
                                    }
                                    break;
                            }
                        } catch (Exception e) {
                            logUtil.log(LogUtil.CRITICAL, e.getMessage() + ", "+ e.getCause());
                            e.printStackTrace();
                        }
                    }
                }
                cycle++;
                rangeChart(cycle);
                updateInfo();
            }
        }
    };
    exec.execute(r);
} 

2 个答案:

答案 0 :(得分:1)

我的印象是你将Callable类Pinger误用为普通类,尽管它只是一个不实现任何多线程服务的接口。 你想做的事情看起来应该更像这样:

//init
Future<String> futures = new Future[targets.length];
String results = new String[targets.length];
ExecutorService service =  Executors.newCachedThreadPool();

//start Threads
for (int i = 0; i<targets.length; i++){      
    Pinger pinger= new Pinger(targets[i]);
    future[i] = service.submit(pinger);
}

//wait for Threads to finish and get results
for(int i = 0; i<futures.length; i++)
    results[i] = futures[i].get()

你的Pinger应该是这样的:

public class Pinger implements Callable<String>{
    Pinger(Target target){ ... }
    public String call(){ ... }
}

在这里,您可以找到完全实施的Example for Callables。在您的代码中,您只向ExecutorService提交一个Runnable,因此只有两个线程(Main和Runnable)。你永远不会调用方法call(),这是由ExecutorService完成的。将此与Runnable接口相比较,您必须执行Thread调用start或将其提交给ExecutorService而不是调用run();您使用submit()期间返回的Future。试着理解Callable的概念,然后你就可以写出你想要的一切。 ; - )

答案 1 :(得分:0)

所以,继承了当前的工作实施......

public void beginPing() {
    safeTargets = new ArrayList<>(); //thread-safe collection
    for (Target t : targets) {
        safeTargets.add(t);
    }
    safeTargets = Collections.synchronizedList(targets);

    exec = Executors.newCachedThreadPool();

    for (int i = 0; i < 4; i++) { //number of threads
        exec.execute(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    for (Target t : safeTargets) {
                        String ping = null;
                        if (t.isActive() && !t.isIsBeingPinged()) { //checks if target is already being pinged by another thread and if it flagged as active and wishes to be pinged.
                            t.setIsBeingPinged(true);
                            t.setPinged(t.getPinged() + 1); //just to see how many times it has been pinged
                            t.setStatus("PINGING");
                            try {
                                Callable c = new Pinger(t);
                                ping = c.call().toString();
                                switch (ping) {
                                    case "TIME_OUT":
                                        t.setStatus("TIME OUT");
                                        t.setLastrtt("TIME_OUT");
                                        t.setTimeouts(t.timeoutsProperty().get() + 1);
                                        logUtil.log(LogUtil.INFO, t.nameProperty().get() + " - timed out!");
                                        t.setIsBeingPinged(false);
                                        break;
                                    case "UNKNOWN_HOST":
                                        t.setStatus("ERROR");
                                        t.setLastrtt("UNKNOWN HOST");
                                        logUtil.log(LogUtil.WARNING, t.nameProperty().get() + " - unknown host!");
                                        t.setIsBeingPinged(false);
                                        break;
                                    case "UNREACHABLE":
                                        t.setStatus("ERROR");
                                        t.setLastrtt("UNREACHABLE HOST");
                                        logUtil.log(LogUtil.WARNING, t.nameProperty().get() + " - is unreachable!");
                                        t.setIsBeingPinged(false);
                                        break;
                                    default:
                                        t.setLastrtt(ping);
                                        t.setStatus("ACTIVE");
                                        t.setIsBeingPinged(false);
                                        break;
                                }
                                System.out.println("C=" + t.getPinged() + " - " + t.nameProperty().get());
                            } catch (Exception e) {
                                logUtil.log(LogUtil.CRITICAL, e.getMessage() + ", " + e.getCause());
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        });
    }
}

毕竟我不得不摆脱对图表的直接添加。

  • 目标列表对象被添加到线程安全的synchronizedList(由kleopatra建议)。
  • 将一个布尔变量添加到Target模型中,以确定它当前是否被其中一个线程ping。 (t.isIsBeingPinged())
  • 使用同一池中的新Runnable将数据添加到图表中,该池迭代目标列表并每秒将最后一个RTT添加到图表中,以避免更高ping的目标落在图表上。

感谢您的快速回复!