JavaRush /Java-Blog /Random-DE /Sie können Java nicht mit einem Thread verderben: Teil I ...
Viacheslav
Level 3

Sie können Java nicht mit einem Thread verderben: Teil I – Threads

Veröffentlicht in der Gruppe Random-DE

Einführung

Multithreading ist seit seinen Anfängen in Java integriert. Werfen wir also einen kurzen Blick darauf, worum es beim Multithreading geht. Sie können Java nicht mit einem Thread ruinieren: Teil I – Threads – 1Nehmen wir als Ausgangspunkt die offizielle Lektion von Oracle: „ Lektion: Die „Hello World!“-Anwendung “. Ändern wir den Code unserer Hello World-Anwendung ein wenig wie folgt:
class HelloWorldApp {
    public static void main(String[] args) {
        System.out.println("Hello, " + args[0]);
    }
}
argsist ein Array von Eingabeparametern, die beim Programmstart übergeben werden. Speichern wir diesen Code in einer Datei mit einem Namen, der dem Namen der Klasse und der Erweiterung entspricht .java. Kompilieren wir mit dem Dienstprogramm javac : javac HelloWorldApp.java Rufen Sie anschließend unseren Code mit einem Parameter auf, zum Beispiel Roger: java HelloWorldApp Roger Sie können Java nicht mit einem Thread ruinieren: Teil I – Threads – 2Unser Code weist jetzt einen schwerwiegenden Fehler auf. Wenn wir kein Argument übergeben (d. h. einfach Java HelloWorldApp ausführen), erhalten wir eine Fehlermeldung:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
        at HelloWorldApp.main(HelloWorldApp.java:3)
In einem Thread mit dem Namen ist eine Ausnahme (d. h. ein Fehler) aufgetreten main. Es stellt sich heraus, dass es in Java eine Art Thread gibt? Hier beginnt unsere Reise.

Java und Threads

Um zu verstehen, was ein Thread ist, müssen Sie verstehen, wie eine Java-Anwendung gestartet wird. Ändern wir unseren Code wie folgt:
class HelloWorldApp {
    public static void main(String[] args) {
		while (true) {
			//Do nothing
		}
	}
}
Jetzt kompilieren wir es erneut mit Javac. Als nächstes führen wir der Einfachheit halber unseren Java-Code in einem separaten Fenster aus. Unter Windows können Sie dies folgendermaßen tun: start java HelloWorldApp. Sehen wir uns nun mithilfe des Dienstprogramms jpsSie können Java nicht mit einem Thread ruinieren: Teil I – Threads – 3 an, welche Informationen uns Java mitteilen wird: Die erste Zahl ist die PID oder Prozess-ID, die Prozesskennung. Was ist ein Prozess?
Процесс — это совокупность Codeа и данных, разделяющих общее виртуальное Adresseное пространство.
Mit Hilfe von Prozessen wird die Ausführung verschiedener Programme voneinander isoliert: Jede Anwendung nutzt ihren eigenen Speicherbereich, ohne andere Programme zu beeinträchtigen. Ich rate Ihnen, den Artikel genauer zu lesen: „ https://habr.com/post/164487/ “. Ein Prozess kann nicht ohne Threads existieren. Wenn also ein Prozess existiert, existiert mindestens ein Thread darin. Wie passiert das in Java? Wenn wir ein Java-Programm ausführen, beginnt seine Ausführung mit der main. Wir betreten das Programm gewissermaßen, daher mainwird diese spezielle Methode als Einstiegspunkt oder „Einstiegspunkt“ bezeichnet. Die Methode mainmuss immer public static voidso sein, dass die Java Virtual Machine (JVM) mit der Ausführung unseres Programms beginnen kann. Weitere Einzelheiten finden Sie unter „ Warum ist die Java-Hauptmethode statisch? “. Es stellt sich heraus, dass der Java Launcher (java.exe oder javaw.exe) eine einfache Anwendung (einfache C-Anwendung) ist: Er lädt verschiedene DLLs, die eigentlich die JVM sind. Der Java-Launcher führt einen bestimmten Satz von JNI-Aufrufen (Java Native Interface) durch. JNI ist der Mechanismus, der die Welt der Java Virtual Machine und die Welt von C++ verbindet. Es stellt sich heraus, dass der Launcher nicht die JVM, sondern ihr Loader ist. Es kennt die richtigen Befehle, die zum Starten der JVM ausgeführt werden müssen. Kann die gesamte erforderliche Umgebung mithilfe von JNI-Aufrufen organisieren. Zu dieser Organisation der Umgebung gehört auch die Erstellung eines Hauptthreads, der üblicherweise als main. Um klarer zu sehen, welche Threads in einem Java-Prozess leben, verwenden wir das Programm jvisualvm , das im JDK enthalten ist. Wenn wir die PID eines Prozesses kennen, können wir sofort Daten darüber öffnen: jvisualvm --openpid айдипроцесса Sie können Java nicht mit einem Thread ruinieren: Teil I – Threads – 4Interessanterweise verfügt jeder Thread über einen eigenen, separaten Bereich im Speicher, der dem Prozess zugewiesen ist. Diese Speicherstruktur wird Stapel genannt. Ein Stapel besteht aus Frames. Ein Frame ist der Punkt, an dem eine Methode aufgerufen wird, ein Ausführungspunkt. Ein Frame kann auch als StackTraceElement dargestellt werden (siehe Java API für StackTraceElement ). Weitere Informationen zum jedem Thread zugewiesenen Speicher finden Sie hier . Wenn wir uns die Java-API ansehen und nach dem Wort Thread suchen, werden wir sehen, dass es eine Klasse java.lang.Thread gibt . Es ist diese Klasse, die einen Stream in Java darstellt, und mit ihr müssen wir arbeiten. Sie können Java nicht mit einem Thread ruinieren: Teil I – Threads – 5

java.lang.Thread

Ein Thread in Java wird als Instanz der Klasse dargestellt java.lang.Thread. Es lohnt sich sofort zu verstehen, dass Instanzen der Thread-Klasse in Java selbst keine Threads sind. Dabei handelt es sich lediglich um eine Art API für Low-Level-Threads, die von der JVM und dem Betriebssystem verwaltet werden. Wenn wir die JVM mit dem Java-Launcher starten, erstellt sie einen Haupt-Thread mit einem Namen mainund mehrere weitere Service-Threads. Wie im JavaDoc der Thread-Klasse angegeben: When a Java Virtual Machine starts up, there is usually a single non-daemon thread Es gibt zwei Arten von Threads: Daemons und Nicht-Daemons. Daemon-Threads sind Hintergrund-Threads (Dienst-Threads), die einige Arbeiten im Hintergrund ausführen. Dieser interessante Begriff bezieht sich auf „Maxwells Dämon“, über den Sie im Wikipedia-Artikel „ Dämonen “ mehr lesen können. Wie in der Dokumentation angegeben, führt die JVM das Programm (den Prozess) so lange aus, bis:
  • Die Runtime.exit -Methode wird nicht aufgerufen
  • Alle Nicht-Daemon-Threads haben ihre Arbeit abgeschlossen (sowohl ohne Fehler als auch mit ausgelösten Ausnahmen).
Daher das wichtige Detail: Daemon-Threads können bei jedem ausgeführten Befehl beendet werden. Daher kann die Integrität der darin enthaltenen Daten nicht gewährleistet werden. Daher eignen sich Daemon-Threads für einige Serviceaufgaben. In Java gibt es beispielsweise einen Thread, der für die Verarbeitung von Finalize-Methoden oder Threads im Zusammenhang mit dem Garbage Collector (GC) verantwortlich ist. Jeder Thread gehört zu einer Gruppe ( ThreadGroup ). Und Gruppen können ineinander übergehen und eine Hierarchie oder Struktur bilden.
public static void main(String []args){
	Thread currentThread = Thread.currentThread();
	ThreadGroup threadGroup = currentThread.getThreadGroup();
	System.out.println("Thread: " + currentThread.getName());
	System.out.println("Thread Group: " + threadGroup.getName());
	System.out.println("Parent Group: " + threadGroup.getParent().getName());
}
Mit Gruppen können Sie die Verwaltung von Abläufen optimieren und den Überblick behalten. Zusätzlich zu Gruppen verfügen Threads über einen eigenen Ausnahmehandler. Schauen wir uns ein Beispiel an:
public static void main(String []args) {
	Thread th = Thread.currentThread();
	th.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
		@Override
		public void uncaughtException(Thread t, Throwable e) {
			System.out.println("Ein Fehler ist aufgetreten: " + e.getMessage());
		}
	});
    System.out.println(2/0);
}
Eine Division durch Null führt zu einem Fehler, der vom Handler abgefangen wird. Wenn Sie den Handler nicht selbst angeben, funktioniert die Standard-Handler-Implementierung, die den Fehlerstapel in StdError anzeigt. Weitere Informationen finden Sie in der Rezension http://pro-java.ru/java-dlya-opytnyx/obrabotchik-neperexvachennyx-isklyuchenij-java/ ". Darüber hinaus hat der Thread eine Priorität. Weitere Informationen zu Prioritäten finden Sie in der Artikel „ Java-Thread-Priorität im Multithreading “.

Einen Thread erstellen

Wie in der Dokumentation angegeben, haben wir zwei Möglichkeiten, einen Thread zu erstellen. Die erste besteht darin, Ihren Erben zu schaffen. Zum Beispiel:
public class HelloWorld{
    public static class MyThread extends Thread {
        @Override
        public void run() {
            System.out.println("Hello, World!");
        }
    }

    public static void main(String []args){
        Thread thread = new MyThread();
        thread.start();
    }
}
Wie Sie sehen können, wird die Aufgabe in der Methode runund der Thread in der Methode gestartet start. Sie sollten nicht verwechselt werden, denn... Wenn wir die Methode rundirekt ausführen, wird kein neuer Thread gestartet. Es ist die Methode start, die die JVM auffordert, einen neuen Thread zu erstellen. Die Option mit einem Nachkommen von Thread ist schlecht, da wir Thread in die Klassenhierarchie einbeziehen. Der zweite Nachteil besteht darin, dass wir beginnen, gegen den Grundsatz der „alleinigen Verantwortung“ zu verstoßen. SOLID, weil Unsere Klasse ist gleichzeitig für die Verwaltung des Threads und für einige Aufgaben verantwortlich, die in diesem Thread ausgeführt werden müssen. Welches ist richtig? Die Antwort liegt in der Methode run, die wir überschreiben:
public void run() {
	if (target != null) {
		target.run();
	}
}
Hier targetsind einige java.lang.Runnable, die wir beim Erstellen einer Instanz der Klasse an Thread übergeben können. Deshalb können wir Folgendes tun:
public class HelloWorld{
    public static void main(String []args){
        Runnable task = new Runnable() {
            public void run() {
                System.out.println("Hello, World!");
            }
        };
        Thread thread = new Thread(task);
        thread.start();
    }
}
RunnableSeit Java 1.8 ist es auch eine funktionale Schnittstelle. Dadurch können Sie Task-Code für Threads noch schöner schreiben:
public static void main(String []args){
	Runnable task = () -> {
		System.out.println("Hello, World!");
	};
	Thread thread = new Thread(task);
	thread.start();
}

Gesamt

Ich hoffe also, dass aus dieser Geschichte klar wird, was ein Stream ist, wie er existiert und welche grundlegenden Operationen mit ihm ausgeführt werden können. Im nächsten Teil lohnt es sich zu verstehen, wie Threads miteinander interagieren und wie ihr Lebenszyklus aussieht. #Wjatscheslaw
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION