JavaRush /Java-Blog /Random-DE /Multithreading in Java: Essenz, Vorteile und häufige Fall...

Multithreading in Java: Essenz, Vorteile und häufige Fallstricke

Veröffentlicht in der Gruppe Random-DE
Hallo! Zunächst einmal herzlichen Glückwunsch: Sie sind beim Thema Multithreading in Java angelangt! Das ist eine ernsthafte Leistung, es liegt noch ein langer Weg vor uns. Aber machen Sie sich bereit: Dies ist eines der schwierigsten Themen im Kurs. Und der Punkt ist nicht, dass hier komplexe Klassen oder viele Methoden verwendet werden: Im Gegenteil, es sind nicht einmal zwei Dutzend. Vielmehr müssen Sie Ihre Denkweise ein wenig ändern. Bisher wurden Ihre Programme sequentiell ausgeführt. Einige Codezeilen folgten anderen, einige Methoden folgten anderen und im Großen und Ganzen war alles klar. Zuerst etwas berechnen, dann das Ergebnis auf der Konsole anzeigen und dann das Programm beenden. Um Multithreading zu verstehen, ist es am besten, in Begriffen der Parallelität zu denken. Beginnen wir mit etwas ganz Einfachem :) Multithreading in Java: Essenz, Vorteile und häufige Fallstricke - 1Stellen Sie sich vor, Ihre Familie zieht von einem Haus in ein anderes. Ein wichtiger Teil des Umzugs ist das Packen Ihrer Bücher. Sie haben viele Bücher angesammelt und müssen sie in Kartons packen. Jetzt sind nur noch Sie frei. Mama bereitet Essen zu, Bruder sammelt Kleidung ein und Schwester ist in den Laden gegangen. Allein schaffen Sie es zumindest und früher oder später werden Sie die Aufgabe sogar selbst erledigen, aber das wird viel Zeit in Anspruch nehmen. In 20 Minuten kommt Ihre Schwester jedoch aus dem Laden zurück und hat nichts anderes zu tun. Damit sie sich dir anschließen kann. Die Aufgabe blieb dieselbe: die Bücher in Kisten packen. Es läuft einfach doppelt so schnell. Warum? Weil die Arbeit parallel erledigt wird. Zwei verschiedene „Threads“ (Sie und Ihre Schwester) führen gleichzeitig dieselbe Aufgabe aus, und wenn sich nichts ändert, wird der Zeitunterschied im Vergleich zu einer Situation, in der Sie alles alleine erledigen würden, sehr groß sein. Wenn Ihr Bruder seine Aufgabe bald erledigt, kann er Ihnen helfen und es geht noch schneller.

Probleme, die Multithreading in Java löst

Im Wesentlichen wurde Java-Multithreading erfunden, um zwei Hauptprobleme zu lösen:
  1. Führen Sie mehrere Aktionen gleichzeitig aus.

    Im obigen Beispiel führten verschiedene Threads (d. h. Familienmitglieder) mehrere Aktionen parallel aus: Geschirr spülen, in den Laden gehen, Sachen zusammenlegen.

    Es kann ein eher „Programmierer“-Beispiel gegeben werden. Stellen Sie sich vor, Sie hätten ein Programm mit einer Benutzeroberfläche. Wenn Sie auf die Schaltfläche „Weiter“ klicken, sollten einige Berechnungen im Programm ausgeführt werden und der Benutzer sollte den folgenden Schnittstellenbildschirm sehen. Wenn diese Aktionen nacheinander ausgeführt werden, friert das Programm nach dem Klicken auf die Schaltfläche „Weiter“ einfach ein. Dem Benutzer wird derselbe Bildschirm mit der Schaltfläche „Weiter“ angezeigt, bis alle internen Berechnungen abgeschlossen sind und das Programm den Teil erreicht, an dem mit dem Zeichnen der Schnittstelle begonnen wird.

    Nun, lasst uns ein paar Minuten warten!

    Multithreading in Java: Essenz, Vorteile und häufige Fallstricke - 3

    Wir können unser Programm auch neu erstellen oder, wie Programmierer sagen, „parallelisieren“. Lassen Sie die erforderlichen Berechnungen in einem Thread und das Rendern der Schnittstelle in einem anderen Thread durchführen. Die meisten Computer verfügen hierfür über genügend Ressourcen. In diesem Fall ist das Programm nicht „dumm“ und der Benutzer kann ruhig zwischen den Bildschirmen der Benutzeroberfläche wechseln, ohne sich Gedanken darüber zu machen, was im Inneren passiert. Es stört nicht :)

  2. Beschleunigen Sie Berechnungen.

    Hier ist alles viel einfacher. Wenn unser Prozessor über mehrere Kerne verfügt und die meisten Prozessoren mittlerweile Multi-Core-Prozessoren sind, kann unsere Aufgabenliste von mehreren Kernen parallel gelöst werden. Wenn wir 1000 Probleme lösen müssen und jedes davon in einer Sekunde gelöst wird, wird ein Kern die Liste natürlich in 1000 Sekunden bewältigen, zwei Kerne in 500 Sekunden, drei in etwas mehr als 333 Sekunden und so weiter.

Aber wie Sie bereits in der Vorlesung gelesen haben, sind moderne Systeme sehr intelligent und können sogar auf einem Rechenkern Parallelität oder Pseudoparallelität implementieren, wenn Aufgaben abwechselnd ausgeführt werden. Lassen Sie uns von allgemeinen zu spezifischen Dingen übergehen und uns mit der Hauptklasse in der Java-Bibliothek im Zusammenhang mit Multithreading vertraut machen – java.lang.Thread. Streng genommen werden Threads in Java durch Instanzen der Klasse repräsentiert Thread. Das heißt, um 10 Threads zu erstellen und auszuführen, benötigen Sie 10 Objekte dieser Klasse. Schreiben wir das einfachste Beispiel:
public class MyFirstThread extends Thread {

   @Override
   public void run() {
       System.out.println("I'm Thread! My name is " + getName());
   }
}
Um Threads zu erstellen und zu starten, müssen wir eine Klasse erstellen und diese von der erben java.lang. Threadund überschreiben Sie die darin enthaltene Methode run(). Letzteres ist sehr wichtig. In der Methode run()schreiben wir die Logik vor, die unser Thread ausführen muss. Wenn wir nun eine Instanz erstellen MyFirstThreadund ausführen, run()gibt die Methode eine Zeile mit ihrem Namen an die Konsole aus: Die Methode getName()gibt den „System“-Namen des Threads aus, der automatisch zugewiesen wird. Aber warum eigentlich „wenn“? Lasst uns erstellen und testen!
public class Main {

   public static void main(String[] args) {

       for (int i = 0; i < 10; i++) {

           MyFirstThread thread = new MyFirstThread();
           thread.start();
       }
   }
}
Konsolenausgabe: Ich bin Thread! Mein Name ist Thread-2, ich bin Thread! Mein Name ist Thread-1, ich bin Thread! Mein Name ist Thread-0, ich bin Thread! Mein Name ist Thread-3, ich bin Thread! Mein Name ist Thread-6, ich bin Thread! Mein Name ist Thread-7, ich bin Thread! Mein Name ist Thread-4, ich bin Thread! Mein Name ist Thread-5, ich bin Thread! Mein Name ist Thread-9, ich bin Thread! Mein Name ist Thread-8. Wir erstellen 10 Threads (Objekte) MyFirstThread, die erben, Threadund starten sie durch Aufrufen der Methode des Objekts start(). Nach dem Aufruf einer Methode start()beginnt ihre Methode zu arbeiten run()und die darin geschriebene Logik wird ausgeführt. Bitte beachten Sie: Die Threadnamen sind nicht in der richtigen Reihenfolge. Es ist ziemlich seltsam, warum wurden sie nicht nacheinander hingerichtet: Thread-0, Thread-1, Thread-2und so weiter? Dies ist genau ein Beispiel dafür, wann standardmäßiges „sequenzielles“ Denken nicht funktionieren wird. Tatsache ist, dass wir in diesem Fall nur Befehle zum Erstellen und Starten von 10 Threads erteilen. In welcher Reihenfolge sie gestartet werden sollen, entscheidet der Thread-Scheduler: ein spezieller Mechanismus innerhalb des Betriebssystems. Wie genau es aufgebaut ist und nach welchem ​​Prinzip es Entscheidungen trifft, ist ein sehr komplexes Thema, auf das wir uns jetzt nicht näher einlassen. Das Wichtigste ist, dass der Programmierer die Reihenfolge der Thread-Ausführung nicht kontrollieren kann. Um den Ernst der Lage zu erkennen, versuchen Sie, die Methode main()aus dem obigen Beispiel noch ein paar Mal auszuführen. Zweite Konsolenausgabe: I'm Thread! Mein Name ist Thread-0, ich bin Thread! Mein Name ist Thread-4, ich bin Thread! Mein Name ist Thread-3, ich bin Thread! Mein Name ist Thread-2, ich bin Thread! Mein Name ist Thread-1, ich bin Thread! Mein Name ist Thread-5, ich bin Thread! Mein Name ist Thread-6, ich bin Thread! Mein Name ist Thread-8, ich bin Thread! Mein Name ist Thread-9, ich bin Thread! Mein Name ist Thread-7 Dritte Konsolenausgabe: Ich bin Thread! Mein Name ist Thread-0, ich bin Thread! Mein Name ist Thread-3, ich bin Thread! Mein Name ist Thread-1, ich bin Thread! Mein Name ist Thread-2, ich bin Thread! Mein Name ist Thread-6, ich bin Thread! Mein Name ist Thread-4, ich bin Thread! Mein Name ist Thread-9, ich bin Thread! Mein Name ist Thread-5, ich bin Thread! Mein Name ist Thread-7, ich bin Thread! Mein Name ist Thread-8

Probleme, die durch Multithreading entstehen

Im Beispiel mit Büchern haben Sie gesehen, dass Multithreading ganz wichtige Probleme löst und seine Verwendung die Arbeit unserer Programme beschleunigt. In vielen Fällen – oft. Doch nicht umsonst gilt Multithreading als komplexes Thema. Denn bei falscher Anwendung entstehen Probleme, statt sie zu lösen. Wenn ich sage „Probleme schaffen“, meine ich nicht etwas Abstraktes. Es gibt zwei spezifische Probleme, die Multithreading verursachen kann: Deadlock und Race Condition. Deadlock ist eine Situation, in der mehrere Threads auf voneinander belegte Ressourcen warten und keiner von ihnen mit der Ausführung fortfahren kann. Wir werden in zukünftigen Vorlesungen mehr darüber sprechen, aber für den Moment reicht dieses Beispiel aus: Multithreading in Java: Essenz, Vorteile und häufige Fallstricke - 4 Stellen Sie sich vor, dass Thread-1 mit einem Objekt-1 arbeitet und Thread-2 mit Objekt-2. Das Programm ist wie folgt geschrieben:
  1. Thread-1 hört auf, mit Objekt-1 zu arbeiten und wechselt zu Objekt-2, sobald Thread-2 aufhört, mit Objekt 2 zu arbeiten und zu Objekt-1 wechselt.
  2. Thread-2 hört auf, mit Objekt-2 zu arbeiten und wechselt zu Objekt-1, sobald Thread-1 aufhört, mit Objekt 1 zu arbeiten und zu Objekt-2 wechselt.
Auch ohne tiefe Kenntnisse im Multithreading kann man leicht verstehen, dass daraus nichts wird. Die Threads werden niemals ihren Platz wechseln und ewig aufeinander warten. Der Fehler scheint offensichtlich, ist es aber in Wirklichkeit nicht. Sie können es ganz einfach in das Programm zulassen. In den folgenden Vorlesungen werden wir uns Beispiele für Code ansehen, der einen Deadlock verursacht. Quora hat übrigens ein hervorragendes Beispiel aus der Praxis, das erklärt, was ein Deadlock ist . „In einigen Bundesstaaten Indiens verkaufen sie Ihnen kein landwirtschaftliches Land, es sei denn, Sie sind als Landwirt registriert. Allerdings werden Sie nicht als Landwirt registriert, wenn Sie kein landwirtschaftliches Land besitzen.“ Großartig, was soll ich sagen! :) Nun zur Rennbedingung – dem Stand des Rennens. Multithreading in Java: Essenz, Vorteile und häufige Fallstricke – 5Eine Race Condition ist ein Konstruktionsfehler in einem Multithread-System oder einer Multithread-Anwendung, bei dem der Betrieb des Systems oder der Anwendung von der Reihenfolge abhängt, in der Teile des Codes ausgeführt werden. Erinnern Sie sich an das Beispiel mit laufenden Threads:
public class MyFirstThread extends Thread {

   @Override
   public void run() {
       System.out.println("Выполнен поток " + getName());
   }
}

public class Main {

   public static void main(String[] args) {

       for (int i = 0; i < 10; i++) {

           MyFirstThread thread = new MyFirstThread();
           thread.start();
       }
   }
}
Stellen Sie sich nun vor, dass das Programm für den Betrieb eines Roboters verantwortlich ist, der Essen zubereitet! Thread-0 holt die Eier aus dem Kühlschrank. Stream 1 schaltet den Herd ein. Stream-2 holt eine Bratpfanne heraus und stellt sie auf den Herd. Stream 3 entzündet ein Feuer auf dem Herd. Strom 4 gießt Öl in die Pfanne. Stream 5 bricht die Eier und gießt sie in die Bratpfanne. Stream 6 wirft die Muscheln in den Mülleimer. Stream-7 nimmt die fertigen Rühreier vom Herd. Potok-8 legt Rührei auf einen Teller. Stream 9 wäscht Geschirr. Schauen Sie sich die Ergebnisse unseres Programms an: Thread-0 ausgeführt Thread-2 Thread ausgeführt Thread-1 Thread ausgeführt Thread-4 Thread ausgeführt Thread-9 Thread ausgeführt Thread-5 Thread ausgeführt Thread-8 Thread ausgeführt Thread-7 Thread ausgeführt Thread-7 Thread ausgeführt -3 Thread-6 Thread ausgeführt. Macht das Skript Spaß? :) Und das alles, weil die Funktionsweise unseres Programms von der Reihenfolge abhängt, in der die Threads ausgeführt werden. Beim kleinsten Verstoß gegen die Reihenfolge verwandelt sich unsere Küche in eine Hölle und ein verrückt gewordener Roboter zerstört alles um ihn herum. Dies ist auch ein häufiges Problem bei der Multithread-Programmierung, von dem Sie mehr als einmal hören werden. Zum Abschluss des Vortrags möchte ich Ihnen ein Buch zum Thema Multithreading empfehlen.
Multithreading in Java: Essenz, Vorteile und häufige Fallstricke – 6
„Java Concurrency in Practice“ wurde bereits 2006 geschrieben, hat aber nicht an Aktualität verloren. Es behandelt die Multithread-Programmierung in Java, angefangen bei den Grundlagen bis hin zu einer Liste der häufigsten Fehler und Antipatterns. Wenn Sie sich jemals dafür entscheiden, ein Multithread-Programmier-Guru zu werden, ist dieses Buch ein Muss. Wir sehen uns bei den nächsten Vorträgen! :) :)
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION