Threads Multithreading

jabaduu

Bekanntes Mitglied
Nächste Frage, ich würde mich gerne Mal intensiver mit Threads beschäftigen. Ich suche ein Projekt oder eine Aufgabe im der man so "richtig viel mit Threads macht".
Habt Ihr vielleicht einen Vorschlag?
 

mihe7

Top Contributor
Addiere Zahlen in mehreren Worker-Threads. Dabei sollen die Worker-Threads

1. die Eingabewerte aus einer gemeinsamen Queue nehmen,
2. wobei das Ende der Eingabewerte durch eine "poison pill" markiert wird;
3. die Ergebnisse in eine gemeinsame Queue geschrieben werden,
4. wobei das Ende der Ausgabewerte durch eine "poison pill" markiert wird

Der aufrufende Thread soll aus der Ausgabequeue alle Werte blockierend lesen und am Ende summiert ausgeben.
 

jabaduu

Bekanntes Mitglied
Addiere Zahlen in mehreren Worker-Threads. Dabei sollen die Worker-Threads

1. die Eingabewerte aus einer gemeinsamen Queue nehmen,
2. wobei das Ende der Eingabewerte durch eine "poison pill" markiert wird;
3. die Ergebnisse in eine gemeinsame Queue geschrieben werden,
4. wobei das Ende der Ausgabewerte durch eine "poison pill" markiert wird

Der aufrufende Thread soll aus der Ausgabequeue alle Werte blockierend lesen und am Ende summiert ausgeben.
Danke! Hab ich mir abgespeichert. Dann mache ich mich daran.
 

jabaduu

Bekanntes Mitglied
Macht Multithreading beim Lesen von Dateien Sinn? ich dachte, wenn die Dateien alle im Selben Verzeichnis auf dem selben Rechner liegen, dann bringt es nichts, nur dass der SchreibLesekopf ständig hin und her springen muss.
https://stackoverflow.com/questions/33369828/multithreaded-file-read-in-java
Hier stellt jemand diese Frage und die ausgewählte Antwort sagt "As you can see, the performance has been highly improved (up to 69%), with just 5 threads. " ich verstehe das nicht.
 

mihe7

Top Contributor
Macht Multithreading beim Lesen von Dateien Sinn? ich dachte, wenn die Dateien alle im Selben Verzeichnis auf dem selben Rechner liegen, dann bringt es nichts, nur dass der SchreibLesekopf ständig hin und her springen muss.
Blenden wir mal alles aus, was das Problem beeinträchtigen könnte (Dateisysteme, Platten, Caches usw.) und nehmen den schlimmsten Fall an, nämlich dass es nur einen Lesekopf gibt, mit einer Anfrage genau ein Sektor von der Platte gelesen wird und die Daten über die Platte absolut ungünstig verteilt sind.

Würdest Du jetzt einen Daten-Sektor nach dem anderen lesen, würde genau das passieren, was Du geschrieben hast: der Lesekopf muss ständig die neue Position anfahren. Stellst Du dagegen mehrere Anfragen auf einmal, weiß der Controller, welche Sektoren gelesen werden müssen. Er kann die Anforderungen somit in eine bessere Reihenfolge bringen.

Konkret können wir mal Sektoren auf den Spuren 1, 40 und 20 annehmen. Nehmen wir außerdem an, der Kopf wäre gerade über Spur 50.

Liest du die Sektoren nacheinander, muss der Kopf erstmal von Spur 50 auf Spur 1 fahren (50 Sektoren), dann auf Spur 40 zurück (40 Sektoren) und dann auf Spur 20 (20 Sektoren), insgesamt überfährt der Kopf also 110 Sektoren.

Liest du die drei Sektoren dagegen parallel, kann der Controller rechnen und fährt erstmal zu Spur 40 (10 Sektoren), weiter zu Spur 20 (20 Sektoren) und am Ende zu Spur 1 (20 Sektoren), insgesamt überfährt der Kopf also nur 50 Sektoren.

Nachtrag: das Wichtigste hätte ich jetzt beinahe vergessen. Bei solchen Dingen spielen viele Faktoren eine Rolle, so dass diese Überlegungen für die Praxis eher uninteressant sind. In einem Fall kann es schneller werden, in einem anderen bringt es dagegen nichts oder hält nur auf. Das einzige Mittel: messen.
 
K

kneitzel

Gast
Also beim Lesen kommen ganz viele Punkte zum tragen:
a) Caching. Du willst nur eine Zeile lesen und das sind von mir aus 70 Bytes. Die heutigen Medien können das aber so nicht lesen. Dort wird Blockweise gelesen, d.h. es wird immer ein ganzer Datenblock gelesen z.B. 4 oder 8 KB. Aus dem Grund gibt es meist ein Caching, damit der Block einmal gelesen wird und so das Medium nicht erneut beansprucht werden muss wenn dann die nächste Zeile gelesen wird.
b) Diverse Speichermedien: Neben den herkömmlichen Festplatten gibt es auch SSDs oder gar Ramdisks (mit gepuffertem Speicher, so dass diese Speicherbereiche sogar einen Reboot überleben können und so!)
Hier hast Du dann einen ganz anderen Zugriff und es spielt keine Rolle mehr, wo Daten gelesen werden müssen. Dieses Versetzen eines Schreib-/Lesekopfes gibt es nicht mehr.

Dann muss man auch noch sehen:
c) Es wird ja nicht nur gelesen, sondern es wird etwas mit den Daten gemacht. Dadurch gibt es dann Verzögerungen. Solche Verzögerungen können also durchaus für andere IO Operationen genutzt werden.

IO Operationen sind übrigens nicht nur Zugriffe auf Festplatten und so. Sondern hier wird generell die Datenübertragung zwischen zwei Komponenten betrachtet. Und hier ist dann immer der Übertragungsweg und die Auslastung der Komponenten zu betrachten. Daher wurden Technologien eingeführt, dass hier auch eine Entlastung stattfinden kann (z.B. Direct Memory Access DMA, womit gewisse Zugriffe auf den Speicher nicht mehr über die CPU laufen müssen.)

Wenn wir dies auf Deinen Fall anwenden, dann ist es so, dass jeder Thread Daten liest (in gewissen Blöcken). Teilweise, nicht immer, wird dadurch ein Zugriff auf die Festplatte notwendig und es wird ein Block an Daten gelesen.) Wenn der Block gelesen wurde kann der Thread die Daten verarbeiten, d.h. es kommt zu mehreren Zugriffen auf den gelesenen Block ohne dass die Festplatte angesprochen werden muss.

Dies bedeutet, dass es eine Reihe Threads geben kann, die parallel arbeiten können. Die Frage ist, wie lange das Lesen des Blocks gedauert hat und wie lange die Abarbeitung des Blocks dauert.
Angenommen das Lesen kostet 1 Zeiteinheit und die Verarbeitung 4 Zeiteinheiten:
Dann können 5 Threads die Platte ausnutzen:
1. Thread liest Block von Platte
2. - 5. Thread wartet auf Zugriff auf Platte
---
1. Thread verarbeitet (1/4)
2. Thread liest Block von Platte
3.-5. Thread wartet auf Zugriff auf Platte
---
1. Thread verarbeitet (2/4)
2. Thread verarbeitet (1/4)
3. Thread liest Block von Platte
4. / 5. Thread wartet
---
1. Thread verarbeitet (3/4)
2. Thread verarbeitet (2/4)
3. Thread verarbeitet (1/4)
4. Thread liest Block von Platte
5. Thread wartet
---
1. Thread verarbeitet (4/4)
2. Thread verarbeitet (3/4)
3. Thread verarbeitet (2/4)
4. Thread verarbeitet (1/4)
5. Thread liest Block von Platte
---
Jetzt startet es wieder von oben, nur eben haben wir jetzt gerade ein perfektes Szenario: Alles kann lesen oder ist am verarbeiten ... Es wartet niemand mehr....
Hier hat man also durch die 5 Threads ein Optimum heraus geholt. Statt 1 Bearbeitung in 5 Zeiteinheit haben wir 5 Bearbeitungen in den 5 Zeiteinheiten (So es lange genug so laufen kann.)

Diese anschauliche, vereinfachte Darstellung: Ist das dadurch verständlicher geworden?

Wichtig ist noch ein weiterer Punkt:
Teilweise muss zwischen Threads umgeschaltet werden. Du hast eine CPU mit x Threads, also können bis zu x Threads mehr oder weniger parallel ablaufen. Wenn Du aber nun mehr als x Threads hast (Was der default ist. Schau Dir nur einmal den Prozessmanager an, wie viele Prozesse aktuell auf Deinem Rechner laufen!), dann müssen die Arbeiten aufgeteilt werden.

Bildlicher Vergleich:
Du hast einen Stift und kannst mit dem schreiben. Nun bekommst Du aber mehrere Zettel und mehrere Anrufer: Du sollst für Anrufer etwas aufschreiben. Da aber niemand warten soll, schaltest Du zwischen den Anrufern ständig hin und her und die Anrufer sagen dann schnell, was Du weiter auf deren Zettel schreiben sollst. So kannst Du natürlich mit einem Stift auf 10 Zetteln mehr oder weniger gleichzeitig schreiben. Aber dieses Umschalten kostet Zeit: Du musst das Telefon auf den Anrufer umschalten und den Zettel des Anrufers zur Hand nehmen ==>
Da das Umschalten Zeit kostet, wird auch deutlich: Angenommen du sollst 100 Anrufer so parallel verarbeiten: Das dauert dann viel viel länger, als wenn Du die Anrufer einer nach dem anderen abarbeitest.
Parallelisierung bringt also nur dann etwas, wenn Du so Pausen ausgleichen kannst. (Ein Anrufer muss erst einmal nachdenken, was er noch aufgeschrieben haben will oder der Anrufer wartet noch auf eine Antwort seiner Frau bezüglich Einkaufszettel ... da wird die parallele Abarbeitung sehr stark.
 
K

kneitzel

Gast
Jo, gute Idee. Mal sehen ob under mod da mal aktiv wird ... @mrBrown hier bist Du gefordert ... Tu mal was für Dein Geld, das Du nicht bekommst :)
 

jabaduu

Bekanntes Mitglied
Also beim Lesen kommen ganz viele Punkte zum tragen:
a) Caching. Du willst nur eine Zeile lesen und das sind von mir aus 70 Bytes. Die heutigen Medien können das aber so nicht lesen. Dort wird Blockweise gelesen, d.h. es wird immer ein ganzer Datenblock gelesen z.B. 4 oder 8 KB. Aus dem Grund gibt es meist ein Caching, damit der Block einmal gelesen wird und so das Medium nicht erneut beansprucht werden muss wenn dann die nächste Zeile gelesen wird.
b) Diverse Speichermedien: Neben den herkömmlichen Festplatten gibt es auch SSDs oder gar Ramdisks (mit gepuffertem Speicher, so dass diese Speicherbereiche sogar einen Reboot überleben können und so!)
Hier hast Du dann einen ganz anderen Zugriff und es spielt keine Rolle mehr, wo Daten gelesen werden müssen. Dieses Versetzen eines Schreib-/Lesekopfes gibt es nicht mehr.

Dann muss man auch noch sehen:
c) Es wird ja nicht nur gelesen, sondern es wird etwas mit den Daten gemacht. Dadurch gibt es dann Verzögerungen. Solche Verzögerungen können also durchaus für andere IO Operationen genutzt werden.

IO Operationen sind übrigens nicht nur Zugriffe auf Festplatten und so. Sondern hier wird generell die Datenübertragung zwischen zwei Komponenten betrachtet. Und hier ist dann immer der Übertragungsweg und die Auslastung der Komponenten zu betrachten. Daher wurden Technologien eingeführt, dass hier auch eine Entlastung stattfinden kann (z.B. Direct Memory Access DMA, womit gewisse Zugriffe auf den Speicher nicht mehr über die CPU laufen müssen.)

Wenn wir dies auf Deinen Fall anwenden, dann ist es so, dass jeder Thread Daten liest (in gewissen Blöcken). Teilweise, nicht immer, wird dadurch ein Zugriff auf die Festplatte notwendig und es wird ein Block an Daten gelesen.) Wenn der Block gelesen wurde kann der Thread die Daten verarbeiten, d.h. es kommt zu mehreren Zugriffen auf den gelesenen Block ohne dass die Festplatte angesprochen werden muss.

Dies bedeutet, dass es eine Reihe Threads geben kann, die parallel arbeiten können. Die Frage ist, wie lange das Lesen des Blocks gedauert hat und wie lange die Abarbeitung des Blocks dauert.
Angenommen das Lesen kostet 1 Zeiteinheit und die Verarbeitung 4 Zeiteinheiten:
Dann können 5 Threads die Platte ausnutzen:
1. Thread liest Block von Platte
2. - 5. Thread wartet auf Zugriff auf Platte
---
1. Thread verarbeitet (1/4)
2. Thread liest Block von Platte
3.-5. Thread wartet auf Zugriff auf Platte
---
1. Thread verarbeitet (2/4)
2. Thread verarbeitet (1/4)
3. Thread liest Block von Platte
4. / 5. Thread wartet
---
1. Thread verarbeitet (3/4)
2. Thread verarbeitet (2/4)
3. Thread verarbeitet (1/4)
4. Thread liest Block von Platte
5. Thread wartet
---
1. Thread verarbeitet (4/4)
2. Thread verarbeitet (3/4)
3. Thread verarbeitet (2/4)
4. Thread verarbeitet (1/4)
5. Thread liest Block von Platte
---
Jetzt startet es wieder von oben, nur eben haben wir jetzt gerade ein perfektes Szenario: Alles kann lesen oder ist am verarbeiten ... Es wartet niemand mehr....
Hier hat man also durch die 5 Threads ein Optimum heraus geholt. Statt 1 Bearbeitung in 5 Zeiteinheit haben wir 5 Bearbeitungen in den 5 Zeiteinheiten (So es lange genug so laufen kann.)

Diese anschauliche, vereinfachte Darstellung: Ist das dadurch verständlicher geworden?

Wichtig ist noch ein weiterer Punkt:
Teilweise muss zwischen Threads umgeschaltet werden. Du hast eine CPU mit x Threads, also können bis zu x Threads mehr oder weniger parallel ablaufen. Wenn Du aber nun mehr als x Threads hast (Was der default ist. Schau Dir nur einmal den Prozessmanager an, wie viele Prozesse aktuell auf Deinem Rechner laufen!), dann müssen die Arbeiten aufgeteilt werden.

Bildlicher Vergleich:
Du hast einen Stift und kannst mit dem schreiben. Nun bekommst Du aber mehrere Zettel und mehrere Anrufer: Du sollst für Anrufer etwas aufschreiben. Da aber niemand warten soll, schaltest Du zwischen den Anrufern ständig hin und her und die Anrufer sagen dann schnell, was Du weiter auf deren Zettel schreiben sollst. So kannst Du natürlich mit einem Stift auf 10 Zetteln mehr oder weniger gleichzeitig schreiben. Aber dieses Umschalten kostet Zeit: Du musst das Telefon auf den Anrufer umschalten und den Zettel des Anrufers zur Hand nehmen ==>
Da das Umschalten Zeit kostet, wird auch deutlich: Angenommen du sollst 100 Anrufer so parallel verarbeiten: Das dauert dann viel viel länger, als wenn Du die Anrufer einer nach dem anderen abarbeitest.
Parallelisierung bringt also nur dann etwas, wenn Du so Pausen ausgleichen kannst. (Ein Anrufer muss erst einmal nachdenken, was er noch aufgeschrieben haben will oder der Anrufer wartet noch auf eine Antwort seiner Frau bezüglich Einkaufszettel ... da wird die parallele Abarbeitung sehr stark.
ja das war verständlich. Danke!
 

jabaduu

Bekanntes Mitglied
Addiere Zahlen in mehreren Worker-Threads. Dabei sollen die Worker-Threads

1. die Eingabewerte aus einer gemeinsamen Queue nehmen,
2. wobei das Ende der Eingabewerte durch eine "poison pill" markiert wird;
3. die Ergebnisse in eine gemeinsame Queue geschrieben werden,
4. wobei das Ende der Ausgabewerte durch eine "poison pill" markiert wird

Der aufrufende Thread soll aus der Ausgabequeue alle Werte blockierend lesen und am Ende summiert ausgeben.

Bin mir nicht sicher ob ich die Aufgabe richtig vestanden habe. Ich habe vorhin den ersten Teil versucht, ist das so gemeint?
Java:
package threadexercise1;

import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

public class threadexercise1{
    static Queue<Integer> input = new LinkedList<Integer>();
    static Queue<Integer> output = new LinkedList<Integer>();
    final static Integer PoisonPill = -1;
    
    static ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(4);
    
    public static void main(String[] args) {
        
       for(int i=0;i<1000;i++) input.add(i);
       input.add(PoisonPill);
       
       for(int j=0;j<4;j++) { 
           Task task = new Task(input, output, PoisonPill);
           executor.execute(task);
       }
    }
}


package threadexercise1;

import java.util.Queue;

public class Task implements Runnable {
    Queue input, output;
    Integer PoisonPill;
    
    public Task(Queue input, Queue output, Integer PoisonPill) {
        this.input = input;
        this.output = output;
        this.PoisonPill = PoisonPill;
    }
    
    @Override
    public void run() {
        // Addiere die Zahlen aus der Queue input 
        int result = add();
        
        //Schreibe das Ergebnis in die Queue output
        writeToQueue(result);
      
    }
    
    
    public synchronized void writeToQueue(int r) {
       if (!output.isEmpty()) output.poll();
       output.add(r);
       output.add(PoisonPill); 
    }
    
    
    public int add() {
        int sum = 0;
        for(Object object : input ){
            Integer i = (Integer) object;
            if(i==PoisonPill) break;
            sum+=i;             
        }
    
        return sum;
    }
}
 
Zuletzt bearbeitet:

mihe7

Top Contributor
Bin mir nicht sicher ob ich die Aufgabe richtig vestanden habe.
Nicht, wie ich das meinte, das liegt aber zum großen Teil an meiner Beschreibung.

De Worker müssen vorher gestartet werden, d. h. sie müssen so funktionieren, dass die Eingabequeue während ihrer Arbeit befüllt werden kann. Die Queue kann während ihrer Arbeit durchaus leer werden. Die poison pill dient dazu, den Workern das Ende mitzuteilen.

Beispiel für den aufrufenden Thread (Pseudocode):
Java:
starteWorker();
for (int i = 0; i < 1000; i++) {
    inputQueue.add(i);
    sleep(100); // 100 ms warten
}
inputQueue.add(poisonPill);

Die Ausgabequeue funktioniert genau andersrum: der aufrufende Thread liest blockierend aus der Ausgabequeue und zählt die Werte zusammen, bis die Poison Pill gelesen wurde. Auch hier gilt, dass die Worker-Threads Zeit benötigen können, d. h. die Ausgabequeue kann zwischendurch durchaus mal leer werden.
 

jabaduu

Bekanntes Mitglied
Ok Danke. Jetzt hab ich die Aufgabe verstanden,aber ich steh auf dem Schlauch. Ich würde hier mit wait() und notify() arbeiten.
Also dass die worker threads warten bis die queue mit Daten gefüllt ist.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
W Multithreading Alphabet Allgemeine Java-Themen 3
T Multithreading: Wie viele Threads sollte ich erstellen? Allgemeine Java-Themen 12
K Multithreading plattform übergreifend? Allgemeine Java-Themen 3
R Bruteforce hashes mit multithreading. Funktioniert das so? Allgemeine Java-Themen 0
B Threads Multithreading Threads sollen warten Allgemeine Java-Themen 12
K Multithreading: Service(Task), wait und continue Allgemeine Java-Themen 21
M JUnit & Multithreading - sehr seltener Fehler Allgemeine Java-Themen 3
C Ressourcensparendes Multithreading Allgemeine Java-Themen 3
A Multithreading mit JButtons Allgemeine Java-Themen 5
S Threads Multithreading- langsamer als Singlethreading-Programm Allgemeine Java-Themen 6
D Threads Multithreading Allgemeine Java-Themen 25
A MultiThreading.. Scheduling-Problem? Allgemeine Java-Themen 10
M Multithreading Problem Allgemeine Java-Themen 3
dayaftereh Multithreading Allgemeine Java-Themen 16
E Multithreading and volatile Allgemeine Java-Themen 10
J Wie die gleichzeitige Ausführung mehrerer Tasks trotz Multithreading verhindern? Allgemeine Java-Themen 2
G multithreading, concurrency conveyor belt beispiel Allgemeine Java-Themen 2
A Problem mit Zufallszahlen und Multithreading Allgemeine Java-Themen 14
I Problem mit Multithreading Allgemeine Java-Themen 4
H Singleton und MultiThreading [erledigt] Allgemeine Java-Themen 3
C Collection Multithreading? Allgemeine Java-Themen 33
O Multithreading mit Java 5 u den Concurrency APIs Allgemeine Java-Themen 7
O Multithreading und Multiprozessor Allgemeine Java-Themen 4
K Multithreading bei statischen Methoden Allgemeine Java-Themen 2
T ungewöhnliche Exception (Multithreading und JList) Allgemeine Java-Themen 10
K Frage zu ProgressBars, Algorithmen und Multithreading ->F Allgemeine Java-Themen 2
flashfactor Multithreading-Problem Allgemeine Java-Themen 4

Ähnliche Java Themen

Neue Themen


Oben