Threads Zwei Threads, aber doppelte Ausgabe verhindern (synchronized)

Bitte aktiviere JavaScript!
Hier eine Lösung mit einer einzigen next() Methode und AtomicReference (man könnte auch AtomicInteger nehmen, aber ich wollte nicht den int der Ipv4 nach außen geben - ach ja, es muss kein long sein, ein int reicht; sind ja nur 4 Bytes) plus noch ein paar Hilfsmethoden:
Java:
import java.util.NoSuchElementException;
public class Ipv4 implements Comparable<Ipv4> {
  private final int value;
  public Ipv4(String value) {
    String[] parts = value.split("\\.");
    if (parts.length != 4) {
      throw new IllegalArgumentException("IP string has no valid length");
    }
    int temp = 0;
    for (String part : parts) {
      int parti = Integer.parseInt(part);
      if (parti < 0 || parti > 255)
        throw new IllegalArgumentException("IP part has invalid value: " + part);
      temp = (temp << 8) + parti;
    }
    this.value = temp;
  }
  public Ipv4(int value) {
    this.value = value;
  }
  public Ipv4Range to(Ipv4 end) {
    return new Ipv4Range(this, end);
  }
  public Ipv4 next() {
    if (value == -1)
      throw new NoSuchElementException();
    return new Ipv4(value + 1);
  }
  @Override
  public String toString() {
    return "Ipv4 {" + (value >> 24 & 0xFF)
          + "." + (value >> 16 & 0xFF)
          + "." + (value >> 8  & 0xFF)
          + "." + (value       & 0xFF) + "}";
  }
  @Override
  public int compareTo(Ipv4 o) {
    return Integer.compare(value, o.value);
  }
}
Java:
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
public class Ipv4Range {
  private final Ipv4 to;
  private final AtomicReference<Ipv4> current;
  public Ipv4Range(Ipv4 from, Ipv4 to) {
    this.to = to;
    this.current = new AtomicReference<Ipv4>(from);
  }
  /**
   * @return the next {@link Ipv4} in this range;
   *         or <code>empty</code> if all Ipv4 instances
   *         of this range have been returned
   */
  public Optional<Ipv4> next() {
    Ipv4 curr, next;
    do {
      if ((curr = current.get()).compareTo(to) >= 0)
        return Optional.empty();
    } while (!current.compareAndSet(curr, next = curr.next()));
    return Optional.of(next);
  }
}
Java:
public class Main {
  public static void main(String[] args) {
    Ipv4Range range = new Ipv4("192.168.0.1").to(new Ipv4("192.168.4.25"));
    Runnable r1 = () -> {
      Ipv4 next = null;
      while ((next = range.next()) != null) {
        System.out.println(Thread.currentThread() + " -> " + next);
      }
    };
    for (int i = 0; i < 8; i++)
      new Thread(r1).start();
  }
}
 
Zuletzt bearbeitet:
Also die Lösungen sind doch alle schon recht schön aufbereitet und dem kann ich nur wenig beisteuern.

Eine Lösung, die mir durch den Kopf geht und ich kurz anmerken möchte, gerade weil sie einfach und schnell ohne diese hohe Komplexität zu schreiben ist: Nutzung von Stream.
- Man will etwas für jede IP aus einerm IP-Bereich machen: Hier kann man die IP Adressen schön als Integer Werte sehen, so dass man ein einfachen Stream von Ints hat
- Diesen Stream kann man nun parallel abarbeiten dank .parallel()
- Die eigentliche Aktion landet dann in .foreach verarbeiten.

Eine solche Lösung bietet sich hier durchaus an, da der eigentliche Task relativ einfach ist und es zwischen den Tasks keine Abhängigkeiten / Wechselwirkungen gibt.

So wird aus der Thematik ein relativ simpler, verständlicher Code, der aus wenigen Methoden und eben der Stream Lösung besteht.

Bezüglich dieser Lösung würde ich einfach einmal folgenden Link anführen (Angelika Langer hat aber diesbezüglich deutlich mehr geschrieben, aber das ist ein interessanter Artikel von Ihr. Generell findet sich bei Ihr aber sehr viel Interessantes.
http://www.angelikalanger.com/Articles/EffectiveJava/82.Java8.Performance-Model-of-Streams/82.Java8.Performance-Model-of-Streams.html

Viele Grüße,

Konrad
 
zufrieden bin ich mit dem alle dem noch nicht, da Synchronisierung und Streams Zeit kosten und durch Parallelisierung eigentlich diese eingespart werden sollte. Jetzt ist die eigentliche Rechenoperation nur minimal (nur eine Ausgabe), aber es wär durchaus ja möglich, auch intensivere Operationen parallelisieren zu müssen...

Bearbeitung: Ich bin jetzt Kuchenessen....
 
- Man will etwas für jede IP aus einerm IP-Bereich machen: Hier kann man die IP Adressen schön als Integer Werte sehen, so dass man ein einfachen Stream von Ints hat
Wobei man es ja eigentlich vermeiden will, IPs nur als int zu repräsentieren (zumindest ich), einen Stream von ints würde ich daher als Nachteil sehen
 
zufrieden bin ich mit dem alle dem noch nicht, da Synchronisierung und Streams Zeit kosten und durch Parallelisierung eigentlich diese eingespart werden sollte. Jetzt ist die eigentliche Rechenoperation nur minimal (nur eine Ausgabe), aber es wär durchaus ja möglich, auch intensivere Operationen parallelisieren zu müssen...

Bearbeitung: Ich bin jetzt Kuchenessen....
Wenn du keinerlei synchronisierung willst, Teil das vorher in passende Bereiche für jeden Thread auf, dann kann jeder unabhängig von den anderen arbeiten.

(Hat allerdings auch Nachteile, falls ein Thread signifikant länger braucht, alle anderen sind dann fertig und müssen auf diesen einen warten)
 
Wobei man es ja eigentlich vermeiden will, IPs nur als int zu repräsentieren (zumindest ich), einen Stream von ints würde ich daher als Nachteil sehen
Es geht ja hier nicht darum, wie es ausgegeben wird. Ausgeben würde ich eine IP auch immer rein die typischen 4 Zählen, getrennt durch Punkte. Aber intern sind und bleiben es eben genau die 4 Bytes und mit diesen lässt sich nun einmal sehr gut rechnen.

Aber natürlich: ich würde Code so nicht wirklich stehen lassen. Daten, die etwas bedeuten, werden gekapselt und dann entsprechend benutzt.

Aber generell würde ich bei so Themen halt eine Standard Lösung bevorzugen. Abarbeitung eines Vorrats ist halt eine Thematik, bei der z.B. TheadPools und so durchaus brauchbar sind. Aber bezüglich diverser möglicher Pattern wurde ja schon viel geschrieben, so dass ich da nicht weiter drauf eingehen möchte. Aber die Parallelen Streams sind halt eine einfache, unkomplizierte schnelle Lösung für sowas.
 
So hätte ich das mit Streams gemacht:
Java:
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Optional;
import java.util.Scanner;
import java.util.stream.IntStream;

public class Unittest {
  private static Scanner s1;
  private static String p1, p2, p3;

  public static int ipToInt(String ip) throws UnknownHostException {
    byte[] a = InetAddress.getByName(ip).getAddress();
    return (a[0] << 24) | (a[1] << 16) | (a[2] << 8) | (a[3] << 0);
  }

  public static String ipToString(int ip) {
    return String.format("%d.%d.%d.%d", (ip >>> 24) & 0xFF, (ip >>> 16) & 0xFF, (ip >>> 8) & 0xFF, (ip >>> 0) & 0xFF);
  }

  public static synchronized Optional<String> getNext() {
    if (p3.equals(p2)) {
      return Optional.empty();
    }
    try {
      p3 = ipToString(ipToInt(p3) + 1);
    } catch (UnknownHostException e) {
      e.printStackTrace();
    }
    return Optional.of(p3);
  }

  public static void main(String[] args) throws UnknownHostException {
    s1 = new Scanner(System.in);
    System.out.println("Adresse 1: ");
    p1 = ipToString(ipToInt(s1.nextLine()) - 1);
    System.out.println("Adresse 2: ");
    p2 = s1.nextLine();
    p3 = p1;

    IntStream.rangeClosed(0, ipToInt(p2) - ipToInt(p1)).mapToObj(e -> getNext()).filter(e -> e.isPresent())
        .forEach(System.out::println);

    p3 = p1;
    IntStream.rangeClosed(0, ipToInt(p2) - ipToInt(p1)).parallel().mapToObj(e -> getNext()).filter(e -> e.isPresent())
        .forEach(System.out::println);
  }
}

Code:
Adresse 1: 
255.255.255.250
Adresse 2: 
255.255.255.255
Optional[255.255.255.250]
Optional[255.255.255.251]
Optional[255.255.255.252]
Optional[255.255.255.253]
Optional[255.255.255.254]
Optional[255.255.255.255]
Optional[255.255.255.250]
Optional[255.255.255.251]
Optional[255.255.255.252]
Optional[255.255.255.253]
Optional[255.255.255.254]
Optional[255.255.255.255]

Jetzt kann System.out::println eine sehr lange Operation sein wie zB Thread.sleep(100); dann würd sequential 600ms brauchen und parallel würd theoretisch nur 100ms brauchen... Richtig?
 
Wobei mein Streams Ansatz sogar deutlich weniger umfasst hätte, denn ich hätte da tatsächlich versucht es auf weniger zu konzentrieren. (Sprich: ohne viel drumherum für jede IP einen Consumer starten, Filter & Co würde ich da nicht betrachten. (Da kommen mir irgendwie zu viele Dinge in ein kurzes Stück Code ... ich teile gerne etwas mehr auf.)

Aber über die Sinnhaftigkeit bin ich mir selbst noch nicht ganz im Klaren. Hier im kleinen ist es ein einfacher Weg für die parallele Ausführung ohne viel Code schreiben zu müssen. Aber Java wird ja auch Thread Pools und so kennen und in einer Applikation will man ggf. ja mehr einstellen an Tasks während die anderen noch abgearbeitet werden. Daher fürchte ich, dass mir so eine Lösung evtl. Flexibilität nehmen könnte. Im Kleinen aber durchaus interessant.

Falls ich es heute Abend noch einmal an den Rechner schaffe, dann würde ich mal den Code basteln, wie ich es mir gedacht habe.. so am Tablet ist das gerade nicht wirklich möglich.
 
Also um einmal zu skizzieren, was mir so als Idee vorgeschwebt ist:

Java:
import java.net.Inet4Address;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.function.Consumer;
import java.util.stream.IntStream;

public class ParallelInet4Action {

    Inet4Address startIp;
    Inet4Address endIp;

    public ParallelInet4Action(final Inet4Address startIp, final Inet4Address endIp) {
        if (intFromIp(endIp) < intFromIp(startIp)) throw new IllegalArgumentException("Start IP was before End IP!");

        this.startIp = startIp;
        this.endIp = endIp;
    }

    public void doAction(final Consumer<Inet4Address> action) {
        IntStream
                .range(intFromIp(startIp), intFromIp(endIp))
                .parallel()
                .forEach(i -> action.accept(ipFromInt(i)));
    }

    private int intFromIp(final Inet4Address ip) {
        return ByteBuffer.wrap(ip.getAddress()).getInt();
    }

    private Inet4Address ipFromInt(final int ip) {
        try {
            return (Inet4Address) Inet4Address.getByAddress(ByteBuffer.allocate(4).putInt(ip).array());
        } catch (UnknownHostException ex) {
            System.out.println("Unknown Host Exception - given ip address was invalid!");
            throw new IllegalStateException("Given Address was invalid!");
        }
    }

    public static void main (String[] args) throws UnknownHostException {
        Inet4Address start = (Inet4Address) Inet4Address.getByAddress( new byte[]{127, 1, 2, 3});
        Inet4Address end = (Inet4Address) Inet4Address.getByAddress( new byte[]{127, 1, 2, 18});

        ParallelInet4Action action = new ParallelInet4Action(start, end);
        action.doAction(i -> System.out.println(i));
    }
}
Das ist jetzt einfach eine Klasse, dessen Instanz eine Start und eine End IP hat. Und auf dieser IP Range kann dann eine Action ausgeführt werden, welche als Consumer angegeben wird dem eine Inet4Address übergeben wird.

Die main ist nur ein ganz kleines Beispiel - statt der Ausgabe kann da dann halt die Erreichbarkeit geprüft werden mir Ausgabe oder was auch immer gewünscht wird.
 
Danke @kneitzel für Deine Bemühungen. Was an #47 unschön ist... Das try catch dürfte dort nicht stehen, potentiell könnte das das Programm nicht anhalten.

Eigentlich bräuchte man 4 Klassen/Interfaces, eine eine IP repräsentierende, einen Producer (intFromIp), einen Consumer (doAction) und eine für die Eingabe.
 
Hallo Tobias,

über diese Exception bin ich ja auch gestolpert und ich habe da etwas mehr drüber nachgedacht.

Bei mir ist es noch relativ einfach, da ich mit einem Byte Array heran gehe. Und das Byte Array vom Integer hat immer die Länge 4 und damit kann bei mir die Exception nicht geworfen werden.

Bei Dir ist es aber genau so. Die Eingabe wird geprüft in main - da kann die Exception dann geworfen werden.
Danach hast Du aber generell valide IP Adressen und getByName kann hier keine Exception mehr werfen, denn bei einer ip Adresse wird diese lediglich validiert. Und da Du da ja auch über einen int arbeitest, wirst Du da auch immer 4 Zahlen in der ip haben, die gültig sind.

Daher ist es aus meiner Sicht gar kein wirkliches Problem. Man könnte da aber natürlich auch drüber nachdenken, dass man in dem Fall eben nichts behandeln kann und dann auch eine (unchecked) Exception wirft (So bei einer Veränderung des Codes ein Fehler in den Code kommen sollte, geht dieser Fehlerfall dann nicht verloren).
 
Danke für den Hinweis. :)
Gerne, waren halt gestern Abend meine Gedanken dazu. Kann man bestimmt geteilter Meinung zu sein, wie man sowas behandelt.

Eine andere Überlegung wäre hier ggf. ein Error zu loggen um dann ggf.mit einem default weiter zu machen. Also in Deinem Code wäre ein Optional.empty als Rückgabe evtl. denkbar.

Wobei mir in meinem Code (#51) noch aufgefallen ist: Der neuen Exception sollte man natürlich die gefangene Exception als cause mitgeben. Das habe ich da ganz vergessen.
 
Passende Stellenanzeigen aus deiner Region:

Oben