Java-Thread locker zu verstehen

Sie müssen Threads verarbeiten, um eine asynchrone und parallele Verarbeitung in Java durchzuführen. Dieser Artikel wurde mit der Hoffnung geschrieben, dass Anfänger bis Fortgeschrittene in Java eine grobe Vorstellung von Threads bekommen.

Was ist ein Thread?

Threads sind wie der Pfad, den Sie beim Ausführen eines Programms einschlagen. Zum Beispiel

  public static void main(String[] args) {
    run();
  }

  static void run() {
    int result = sum(1, 2);
    System.out.println(result);
  }

  static int sum(int a, int b) {
    return a + b;
  }

Wenn Sie das Programm ausführen main () wird aufgerufen, run () wird aufgerufen, sum () wird aufgerufen, System.out.println () wird aufgerufen usw., während die Prozesse einzeln ausgeführt werden. Gehen. Ein Thread ist derjenige, der die Verarbeitung ausführt, während er diesem geraden Pfad folgt.

Die gesamte Verarbeitung läuft auf Threads

Zum Beispiel, wenn Sie den Prozess von der Hauptfunktion aus ausführen ...

  public static void main(String[] args) {
    Thread currentThread = Thread.currentThread(); //Holen Sie sich Ihren eigenen Thread
    System.out.printf("ID:%d, Name:%s, Priority:%d, State:%s%n",
        currentThread.getId(),
        currentThread.getName(),
        currentThread.getPriority(),
        currentThread.getState());
  }

Ausführungsergebnis

ID:1, Name:main, Priority:5, State:RUNNABLE

Sie können sehen, dass es in einem Thread namens "main" ausgeführt wurde

Versuchen Sie, Code zu schreiben, um etwas in einem separaten Thread zu tun

Verzweigen Sie einen anderen Thread vom Hauptthread und versuchen Sie, etwas asynchron auszuführen. Neu die Thread Klasse und call start ().

public class Sample {

  public static void main(String[] args) throws Exception {
    Thread thread = new Thread(() -> { //Erstelle einen Thread. Übergeben Sie den Prozess, den Sie ausführen möchten, an den Konstruktor
      for (int i = 1; i < 5; i++) {
        System.out.println("thread: " + i);
        try {
          Thread.sleep(100L);
        } catch (InterruptedException e) {
          return;
        }
      }
    });

    System.out.println("main: 1");

    thread.start(); //Starten Sie die Verarbeitung des Threads

    System.out.println("main: 2");
    Thread.sleep(150L);
    System.out.println("main: 3");

    thread.join(); //Warten Sie auf das Ende des Threads (Wenn Sie nicht warten, gibt es kein Problem, wenn Sie es nicht tun)
    System.out.println("main: 4");
  }
}

Ausgabeergebnis

main: 1
main: 2
thread: 1
thread: 2
main: 3
thread: 3
thread: 4
main: 4

Sie können sehen, dass die Verarbeitung des Hauptthreads und die Verarbeitung des generierten Threads unabhängig voneinander ausgeführt werden.

Welche Informationen hat der Thread?

Java verarbeitet Threads über die Klasse "Thread". Wenn Sie sich die Eigenschaften der Thread-Klasse in Intellij ansehen, sieht es so aus

Screen Shot 2018-12-17 at 11.40.57.png

Ich werde nur die erklären, die Sie oft sehen.

ID :long

Ein langer Typwert, der automatisch zugewiesen wird, wenn ein Thread erstellt wird. Einzigartig und unveränderlich.

name :String

Der Name des Threads. Jeder Name kann mit setName (String) gesetzt werden. Wenn Sie einen Thread mit "new Thread ()" erstellen, werden Namen wie "Thread-1" und "Thread-2" willkürlich festgelegt.

state :Thread.State

Thread-Status. "Laufen" oder "Warten". Der Thread hat einen Status und befindet sich in einem der folgenden Status: "NEW", "RUNNABLE", "BLOCKED", "WAITING", "TIMED_WAITING", "TERMINATED".

Weitere Informationen finden Sie unter Javadoc.

interrupted :boolean

Ob der Thread unterbrochen wurde.

--Dieser Wert kann mit Thread # interrupt () auf true gesetzt werden

priority :int

Thread-Priorität. Wenn ein Ressourcenkonflikt auftritt, wird der Thread mit der höheren Priorität mit Priorität ausgeführt. Der Standardwert ist 5 (Thread.NORM_PRIORITY), der niedrigste ist 1 ( Thread.MIN_PRIORITY) und der höchste ist 10 (Thread.MAX_PRIORITY). Kann mit setPriority (int) geändert werden.

daemon: boolean

Ist es ein Daemon-Thread? Wenn daemon`` true ist, wird es als Daemon-Thread bezeichnet, und wenn es false ist, wird es als User-Thread bezeichnet. Der Standardwert ist "false", der mit "setDaemon (boolean)" festgelegt werden kann.

Verschiedene Thread-Programmierung

Es gibt viele Möglichkeiten, ein Programm zu schreiben, das etwas in einem separaten Thread ausführt.

Wie man rohen Thread verwendet

Der Thread wird gestartet, indem der auszuführende Prozess in das Konstruktorargument von "Thread" mit Lambda oder "Neu" gesetzt und "Start ()" aufgerufen wird.

Thread thread = new Thread(() -> {
    //Etwas zu verarbeiten...
});
thread.start(); //Starten Sie einen Thread. "Etwas" wird asynchron ausgeführt

Sie können Thread erben und run überschreiben. (Ich persönlich empfehle jedoch die Methode ↑, da die Verarbeitung von der Thread-Klasse abhängt.)

class MyThread extends Thread {
  @Override
  public void run() {
    //Etwas zu verarbeiten...
  }
}

MyThread thread = new MyThread();
thread.start(); //Starten Sie einen Thread. "Etwas" wird asynchron ausgeführt

So verwenden Sie Executor

Sie können ganz einfach so etwas wie einen Thread-Pool erstellen.

ExecutorService threadPool = Executors.newFixedThreadPool(3); //Anzahl der Themen=Erstellen Sie 3 Thread-Pools

//Versuchen Sie, 100 zu verarbeiten
for (int i = 0; i < 100; i++) {
  threadPool.submit(() -> { //threadPool verwendet 3 Threads vollständig, um die Eingabeverarbeitung stetig auszuführen
    //Etwas zu verarbeiten...
  });
}

//Warten Sie, bis alle eingereichten Verarbeitungen abgeschlossen sind
threadPool.awaitTermination(1, TimeUnit.MINUTES);

//Thread-Pool stoppen
threadPool.shutdown();

Neben "Executors.newFixedThreadPool ()" gibt es noch viele andere Dinge.

Verwendung von Completable Future

Es ist wie Future / Promise in JavaScript. Interner Executor wird zur Thread-Steuerung verwendet

//Verarbeitung asynchron ausführen
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
  //Etwas zu verarbeiten...
});

//Sie können einen Rückruf im Rückgabewert CompletableFuture registrieren
future.whenComplete((aVoid, throwable) -> { //Im JavaScript-Versprechen.then()
  //Dies wird aufgerufen, wenn "etwas getan wird"
  // -Wenn "etwas" normal abgeschlossen ist, ist das auslösbare Argument null.
  // -Wenn eine Ausnahme in "etwas" auftritt, wird die Ausnahme an das Argument throwable übergeben.
});

Fadenbruch

Erteilen Sie eine Suspend-Bestellung

Verwenden Sie "Thread # Interrupt ()", wenn Sie die Verarbeitung eines laufenden Threads unterbrechen möchten

Thread thread = createNanikanoThread();
thread.start(); //Führen Sie den Thread aus
...
thread.interrupt(); //Senden Sie einen Suspend-Befehl an einen laufenden Thread

Nur weil Interrupt () bedeutet dies nicht, dass der Ziel-Thread sofort gestoppt wird! Was passiert, wenn Sie diese Methode aufrufen, ist Ein Bild, in dem "true" in dem booleschen Feld "unterbrochen" des Zielthreads gesetzt ist (← etwas grobe Erklärung). Grundsätzlich müssen Sie dieses "unterbrochene" Flag im Prozess auf der unterbrochenen Seite überprüfen.

Bestätigung des Suspendierungsstatus

Eine möglicherweise unterbrochene Verarbeitung muss vor diesem Hintergrund implementiert werden. Sie können mit Thread.interrupted () überprüfen, ob es unterbrochen wurde (ob das Flag "unterbrochen" aktiviert ist).

//Der Prozess, etwas für immer zu tun
while (true) {
  if (Thread.interrupted()) { //Überprüfen Sie, ob es unterbrochen wurde (* Wenn diese Methode aufgerufen wird, kehrt das unterbrochene Flag zu false zurück.)
    throw new InterruptedException(); //Wirf eine Ausnahme aus und beende den Prozess. Grundsätzlich ist es in Ordnung, wenn Sie InterruptedException verwenden
  }
  //Etwas verarbeiten
  ...
}

Durch Aufrufen von "Thread.interrupted ()" zur Überprüfung des Status wird das unterbrochene Flag auf "false" zurückgesetzt. (Übrigens, wenn Sie mit Thread.currentThread (). IsInterrupted () überprüfen, wird es nicht zurückgesetzt)

Einige Methoden überprüfen den Status "unterbrochen". Zum Beispiel wird "Thread.sleep ()" aufhören zu schlafen und eine "InterruptedException" auslösen, wenn das Flag "Interrupted" während des Schlafes gesetzt wird. (Übrigens wird das unterbrochene Flag auch zu diesem Zeitpunkt auf false zurückgesetzt.)

//Der Prozess, etwas für immer zu tun
while (true) {
  Thread.sleep(1000L); //Unterbricht die Verarbeitung für 1 Sekunde. InterruptedException wird ausgelöst, wenn es im Ruhezustand unterbrochen wird
  ...
}

Behandlung von InterruptedException

Die Tatsache, dass eine InterruptedException ausgelöst wurde, bedeutet, dass der Thread unterbrochen wurde. Eine angemessene Verarbeitung muss erfolgen, ohne dass diese Informationen verloren gehen.

Schlechtes Beispiel 1: Ignorieren Sie die Suspend-Anweisung und setzen Sie die Verarbeitung fort

Ich ignoriere die Information, dass der Thread unterbrochen wurde und fahre fort

try {
  Thread.sleep(100);
} catch (InterruptedException ex) {
}

Schlechtes Beispiel 2: Wrap and Throw mit einer anderen Ausnahme

Die, die du oft siehst

try {
  Thread.sleep(100);
} catch (InterruptedException ex) {
  throw new RuntimeException(ex);
}

Die Information, dass der Thread unterbrochen wurde, ist verloren gegangen. Wenn diese Ausnahme im äußeren Prozess abgefangen wird, wird sie als unerwarteter Fehler behandelt.

Kein schlechtes Beispiel: Schalten Sie das unterbrochene Flag wieder ein und wickeln Sie es mit einer anderen Ausnahme ein

try {
  Thread.sleep(100);
} catch (InterruptedException ex) {
  Thread.currentThread().interrupt(); // Here!
  throw new RuntimeException(ex);
}

Die Beurteilung des unterbrochenen Flags bleibt der externen Verarbeitung überlassen. Die Information, dass es unterbrochen wurde, bleibt erhalten. Natürlich muss der Prozess, dies von außen zu erfassen, etwas tun, um festzustellen, ob es unterbrochen wurde.

Persönlich entwickelte Praxis: Erstellen Sie eine dedizierte ungeprüfte Ausnahme

(Ich weiß nicht, ob diese Methode gut ist. Bitte lassen Sie mich wissen, ob es einen besseren Weg gibt ...)

try {
  Thread.sleep(100);
} catch (InterruptedException ex) {
  throw new InterruptedRuntimeException(ex);
}

Stellen Sie sicher, dass Sie diese InterruptedRuntimeException in der äußeren Verarbeitung abfangen und die entsprechende Verarbeitung durchführen.

Da es schwierig ist, jedes Mal zu versuchen, im Schlaf zu fangen, mache ich eine Utility-Klasse

public class ThreadUtils {

  /**
   * @param n Ruhezeit (Millisekunden)
   * @löst InterruptedRuntimeException aus Wenn der Thread unterbrochen wird
   */
  public static void sleep(long n) {
    try {
      Thread.sleep(n);
    } catch (InterruptedException ex) {
      throw new InterruptedRuntimeException(ex);
    }
  }

  /**
   *Überprüfen Sie, ob der aktuelle Thread unterbrochen wurde.
   * @löst InterruptedRuntimeException aus Wenn der Thread unterbrochen wird
   */
  public static void checkForInterruption() {
    if (Thread.interrupted()) {
      throw new InterruptedRuntimeException(ex);
    }
  }
}

Informationen zur Stapelverfolgung

WIP

Recommended Posts

Java-Thread locker zu verstehen
[java8] Um die Stream-API zu verstehen
Java-Referenz zum Verständnis in der Abbildung
Java-Thread-Verarbeitung
[Java] Map # Merge ist schwer zu verstehen.
[Java] Einführung in Java
Verstehen Sie den Java-Konstruktor
Zwei Möglichkeiten, einen Thread in Java + @ zu starten
Änderungen von Java 8 zu Java 11
Summe von Java_1 bis 100
[Java] Thread und ausführbar
Verstehen Sie Java 8 Lambda-Ausdrücke
Kotlins Verbesserungen an Java
Thread sichere Zusammenfassung ~ Java ~
Einführung in den Java-Befehl
Java Programming Thread Runnable
Listenverarbeitung zum Verstehen mit Bildern --java8 stream / javaslang-
In der Abbildung verstandene Java-Klassen und -Instanzen
Ein Ingenieur, der Java nicht versteht, ging zu JJUG CCC.
[Java] Neue Thread-Generierungsmethode (2)
So senken Sie die Java-Version
Migration von Cobol nach JAVA
[Java] Verwendung von Map
Java fügt PDF eine Tabelle hinzu
So deinstallieren Sie Java 8 (Mac)
Java zum Spielen mit Function
Java - So erstellen Sie JTable
Verwendung von Java Optional
Neue Funktionen von Java7 bis Java8
So minimieren Sie Java-Images
Wie schreibe ich einen Java-Kommentar
Verwendung der Java-Klasse
Stellen Sie eine Verbindung von Java zu PostgreSQL her
[Java] Verwendung von removeAll ()
[Java] So zeigen Sie Wingdings an
[Java] Einführung in den Lambda-Ausdruck
Verwendung von Java Map
So legen Sie Java-Konstanten fest
Stellen Sie mit Java eine Verbindung zur Datenbank her
Stellen Sie mit Java eine Verbindung zu MySQL 8 her
[Java] Gründe für die Verwendung von statischen
Java-Thread sicher für Sie
Listenverarbeitung zum Verstehen mit Bildern --java8 stream / javaslang --bonus
Verwendung von Java-Variablen
[Java] Einführung in die Stream-API
Java8 startet jetzt ~ Optional ~
So konvertieren Sie Java Base
Bei der Java-Parallelverarbeitung bin ich mir nicht sicher
[Java] So implementieren Sie Multithreading
Von ineffektivem Java zu effektivem Java
So initialisieren Sie ein Java-Array
[Java] Neue Thread-Generierungsmethode (1)
[Einführung in Janken (ähnliche) Spiele] Java
Eingabe in die Java-Konsole
Verstehen Sie, wie die funktionale Programmierung in Java auf einen Schlag eingeführt wurde