CodeGym /Java-Blog /Random-DE /Die 50 wichtigsten Fragen und Antworten zu Vorstellungsge...
John Squirrels
Level 41
San Francisco

Die 50 wichtigsten Fragen und Antworten zu Vorstellungsgesprächen für Java Core. Teil 2

Veröffentlicht in der Gruppe Random-DE
Die 50 wichtigsten Fragen und Antworten zu Vorstellungsgesprächen für Java Core. Teil 1Die 50 wichtigsten Fragen und Antworten zu Vorstellungsgesprächen für Java Core.  Teil 2 - 1

Multithreading

24. Wie erstelle ich einen neuen Thread in Java?

Auf die eine oder andere Weise wird ein Thread mithilfe der Thread-Klasse erstellt. Aber es gibt verschiedene Möglichkeiten, dies zu tun ...
  1. Erben Sie java.lang.Thread .
  2. Implementieren Sie die java.lang.Runnable- Schnittstelle – der Konstruktor der Thread- Klasse akzeptiert ein Runnable-Objekt.
Lassen Sie uns über jeden von ihnen sprechen.

Erben Sie die Thread-Klasse

In diesem Fall sorgen wir dafür, dass unsere Klasse java.lang.Thread erbt . Es verfügt über eine run()- Methode, und das ist genau das, was wir brauchen. Das gesamte Leben und die gesamte Logik des neuen Threads werden in dieser Methode enthalten sein. Es ist so etwas wie eine Hauptmethode für den neuen Thread. Danach müssen wir nur noch ein Objekt unserer Klasse erstellen und die Methode start() aufrufen . Dadurch wird ein neuer Thread erstellt und mit der Ausführung seiner Logik begonnen. Lass uns einen Blick darauf werfen:

/**
* An example of how to create threads by inheriting the {@link Thread} class.
*/
class ThreadInheritance extends Thread {

   @Override
   public void run() {
       System.out.println(Thread.currentThread().getName());
   }

   public static void main(String[] args) {
       ThreadInheritance threadInheritance1 = new ThreadInheritance();
       ThreadInheritance threadInheritance2 = new ThreadInheritance();
       ThreadInheritance threadInheritance3 = new ThreadInheritance();
       threadInheritance1.start();
       threadInheritance2.start();
       threadInheritance3.start();
   }
}
Die Konsolenausgabe sieht etwa so aus:
Thread-1 Thread-0 Thread-2
Das heißt, auch hier sehen wir, dass Threads nicht in der richtigen Reihenfolge ausgeführt werden, sondern so, wie die JVM es für richtig hält, sie auszuführen :)

Implementieren Sie die Runnable-Schnittstelle

Wenn Sie gegen die Vererbung sind und/oder bereits eine andere Klasse erben, können Sie die java.lang.Runnable- Schnittstelle verwenden. Hier veranlassen wir unsere Klasse, diese Schnittstelle zu implementieren, indem wir die run()- Methode implementieren, genau wie im obigen Beispiel. Jetzt müssen nur noch Thread- Objekte erstellt werden. Es scheint, dass mehr Codezeilen schlechter sind. Aber wir wissen, wie schädlich eine Vererbung ist und dass es besser ist, sie auf jeden Fall zu vermeiden ;) Schauen Sie mal rein:

/**
* An example of how to create threads from the {@link Runnable} interface.
* It's easier than easy — we implement this interface and then pass an instance of our object
* to the constructor.
*/
class ThreadInheritance implements Runnable {

   @Override
   public void run() {
       System.out.println(Thread.currentThread().getName());
   }

   public static void main(String[] args) {
       ThreadInheritance runnable1 = new ThreadInheritance();
       ThreadInheritance runnable2 = new ThreadInheritance();
       ThreadInheritance runnable3 = new ThreadInheritance();

       Thread threadRunnable1 = new Thread(runnable1);
       Thread threadRunnable2 = new Thread(runnable2);
       Thread threadRunnable3 = new Thread(runnable3);

       threadRunnable1.start();
       threadRunnable2.start();
       threadRunnable3.start();
   }
}
Und hier ist das Ergebnis:
Thread-0 Thread-1 Thread-2

25. Was ist der Unterschied zwischen einem Prozess und einem Thread?

Die 50 wichtigsten Fragen und Antworten zu Vorstellungsgesprächen für Java Core.  Teil 2 - 2Ein Prozess und ein Thread unterscheiden sich auf folgende Weise:
  1. Ein laufendes Programm wird als Prozess bezeichnet, ein Thread ist jedoch ein Teil eines Prozesses.
  2. Prozesse sind unabhängig, Threads sind jedoch Teile eines Prozesses.
  3. Prozesse haben unterschiedliche Adressräume im Speicher, Threads teilen sich jedoch einen gemeinsamen Adressraum.
  4. Der Kontextwechsel zwischen Threads ist schneller als der Wechsel zwischen Prozessen.
  5. Die Kommunikation zwischen Prozessen ist langsamer und teurer als die Kommunikation zwischen Threads.
  6. Änderungen in einem übergeordneten Prozess wirken sich nicht auf einen untergeordneten Prozess aus, Änderungen in einem übergeordneten Thread können sich jedoch auf einen untergeordneten Thread auswirken.

26. Welche Vorteile bietet Multithreading?

  1. Durch Multithreading kann eine Anwendung/ein Programm immer auf Eingaben reagieren, auch wenn bereits einige Hintergrundaufgaben ausgeführt werden.
  2. Multithreading ermöglicht eine schnellere Erledigung von Aufgaben, da Threads unabhängig voneinander ausgeführt werden.
  3. Multithreading ermöglicht eine bessere Nutzung des Cache-Speichers, da Threads auf gemeinsam genutzte Speicherressourcen zugreifen können.
  4. Multithreading reduziert die Anzahl der erforderlichen Server, da ein Server mehrere Threads gleichzeitig ausführen kann.

27. Welche Zustände gibt es im Lebenszyklus eines Threads?

Die 50 wichtigsten Fragen und Antworten zu Vorstellungsgesprächen für Java Core.  Teil 2 - 3
  1. Neu: In diesem Zustand wird das Thread- Objekt mit dem neuen Operator erstellt, es existiert jedoch noch kein neuer Thread. Der Thread startet erst, wenn wir die Methode start() aufrufen.
  2. Ausführbar: In diesem Zustand ist der Thread nach dem start() zur Ausführung bereit. Methode aufgerufen wird. Es wurde jedoch noch nicht vom Thread-Scheduler ausgewählt.
  3. Läuft: In diesem Zustand wählt der Thread-Scheduler einen Thread aus einem Bereitschaftszustand aus und führt ihn aus.
  4. Warten/Blockiert: In diesem Zustand wird ein Thread nicht ausgeführt, ist aber noch aktiv oder wartet auf den Abschluss eines anderen Threads.
  5. Tot/Beendet: Wenn ein Thread die run()- Methode verlässt , befindet er sich in einem toten oder beendeten Zustand.

28. Ist es möglich, einen Thread zweimal auszuführen?

Nein, wir können einen Thread nicht neu starten, denn nachdem ein Thread gestartet und ausgeführt wurde, geht er in den Dead-Zustand über. Wenn wir zweimal versuchen, einen Thread zu starten, wird eine java.lang.IllegalThreadStateException ausgelöst. Lass uns einen Blick darauf werfen:

class DoubleStartThreadExample extends Thread {

   /**
    * Simulate the work of a thread
    */
   public void run() {
	// Something happens. At this state, this is not essential.
   }

   /**
    * Start the thread twice
    */
   public static void main(String[] args) {
       DoubleStartThreadExample doubleStartThreadExample = new DoubleStartThreadExample();
       doubleStartThreadExample.start();
       doubleStartThreadExample.start();
   }
}
Es kommt zu einer Ausnahme, sobald die Ausführung am zweiten Anfang desselben Threads erfolgt. Probieren Sie es selbst aus ;) Es ist besser, das einmal zu sehen, als hundertmal davon zu hören.

29. Was wäre, wenn Sie run() direkt aufrufen, ohne start() aufzurufen?

Ja, Sie können die Methode run() durchaus aufrufen , aber es wird kein neuer Thread erstellt und die Methode wird nicht in einem separaten Thread ausgeführt. In diesem Fall haben wir ein gewöhnliches Objekt, das eine gewöhnliche Methode aufruft. Wenn wir über die start()- Methode sprechen, dann ist das eine andere Sache. Wenn diese Methode aufgerufen wird, startet die JVM einen neuen Thread. Dieser Thread wiederum ruft unsere Methode auf ;) Glauben Sie es nicht? Probieren Sie es hier aus:

class ThreadCallRunExample extends Thread {

   public void run() {
       for (int i = 0; i < 5; i++) {
           System.out.print(i);
       }
   }

   public static void main(String args[]) {
       ThreadCallRunExample runExample1 = new ThreadCallRunExample();
       ThreadCallRunExample runExample2 = new ThreadCallRunExample();

       // Two ordinary methods will be called in the main thread, one after the other.
       runExample1.run();
       runExample2.run();
   }
}
Und die Konsolenausgabe sieht so aus:
0123401234
Wie Sie sehen, wurde kein Thread erstellt. Alles funktionierte wie in einem normalen Unterricht. Zuerst wurde die Methode des ersten Objekts ausgeführt und dann die zweite.

30. Was ist ein Daemon-Thread?

Ein Daemon-Thread ist ein Thread, der Aufgaben mit einer niedrigeren Priorität als ein anderer Thread ausführt. Mit anderen Worten: Seine Aufgabe besteht darin, Hilfsaufgaben auszuführen, die nur in Verbindung mit einem anderen (Haupt-)Thread erledigt werden müssen. Es gibt viele Daemon-Threads, die automatisch ausgeführt werden, z. B. Garbage Collection, Finalizer usw.

Warum beendet Java einen Daemon-Thread?

Der einzige Zweck des Daemon-Threads besteht darin, den Thread eines Benutzers im Hintergrund zu unterstützen. Wenn dementsprechend der Hauptthread beendet wird, beendet die JVM automatisch alle ihre Daemon-Threads.

Methoden der Thread-Klasse

Die Klasse java.lang.Thread bietet zwei Methoden zum Arbeiten mit einem Daemon-Thread:
  1. public void setDaemon(boolean status) – Diese Methode gibt an, ob es sich um einen Daemon-Thread handelt. Der Standardwert ist false . Das bedeutet, dass keine Daemon-Threads erstellt werden, es sei denn, Sie geben dies ausdrücklich an.
  2. public boolean isDaemon() – Diese Methode ist im Wesentlichen ein Getter für die Daemon- Variable, die wir mit der vorherigen Methode festgelegt haben.
Beispiel:

class DaemonThreadExample extends Thread {

   public void run() {
       // Checks whether this thread is a daemon
       if (Thread.currentThread().isDaemon()) {
           System.out.println("daemon thread");
       } else {
           System.out.println("user thread");
       }
   }

   public static void main(String[] args) {
       DaemonThreadExample thread1 = new DaemonThreadExample();
       DaemonThreadExample thread2 = new DaemonThreadExample();
       DaemonThreadExample thread3 = new DaemonThreadExample();

       // Make thread1 a daemon thread.
       thread1.setDaemon(true);

       System.out.println("daemon? " + thread1.isDaemon());
       System.out.println("daemon? " + thread2.isDaemon());
       System.out.println("daemon? " + thread3.isDaemon());

       thread1.start();
       thread2.start();
       thread3.start();
   }
}
Konsolenausgabe:
Daemon? wahrer Dämon? falscher Dämon? false Daemon-Thread-Benutzer-Thread Benutzer-Thread
Aus der Ausgabe sehen wir, dass wir innerhalb des Threads selbst die statische Methode currentThread() verwenden können, um herauszufinden, um welchen Thread es sich handelt. Wenn wir alternativ einen Verweis auf das Thread-Objekt haben, können wir dies auch direkt daraus herausfinden. Dies bietet das erforderliche Maß an Konfigurierbarkeit.

31. Ist es möglich, einen Thread nach seiner Erstellung zu einem Daemon zu machen?

Nein. Wenn Sie dies versuchen, erhalten Sie eine IllegalThreadStateException . Das bedeutet, dass wir einen Daemon-Thread nur vor dem Start erstellen können. Beispiel:

class SetDaemonAfterStartExample extends Thread {

   public void run() {
       System.out.println("Working...");
   }

   public static void main(String[] args) {
       SetDaemonAfterStartExample afterStartExample = new SetDaemonAfterStartExample();
       afterStartExample.start();
      
       // An exception will be thrown here
       afterStartExample.setDaemon(true);
   }
}
Konsolenausgabe:
Funktioniert... Ausnahme im Thread „main“ java.lang.IllegalThreadStateException bei java.lang.Thread.setDaemon(Thread.java:1359) bei SetDaemonAfterStartExample.main(SetDaemonAfterStartExample.java:14)

32. Was ist ein Shutdown-Hook?

Ein Shutdown-Hook ist ein Thread, der implizit aufgerufen wird, bevor die Java Virtual Machine (JVM) heruntergefahren wird. Somit können wir es verwenden, um eine Ressource freizugeben oder den Status zu speichern, wenn die Java Virtual Machine normal oder abnormal heruntergefahren wird. Mit der folgenden Methode können wir einen Shutdown-Hook hinzufügen:

Runtime.getRuntime().addShutdownHook(new ShutdownHookThreadExample());
Wie im Beispiel gezeigt:

/**
* A program that shows how to start a shutdown hook thread,
* which will be executed right before the JVM shuts down
*/
class ShutdownHookThreadExample extends Thread {

   public void run() {
       System.out.println("shutdown hook executed");
   }

   public static void main(String[] args) {

       Runtime.getRuntime().addShutdownHook(new ShutdownHookThreadExample());

       System.out.println("Now the program is going to fall asleep. Press Ctrl+C to terminate it.");
       try {
           Thread.sleep(60000);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
   }
}
Konsolenausgabe:
Jetzt schläft das Programm ein. Drücken Sie Strg+C, um es zu beenden. Shutdown-Hook ausgeführt

33. Was ist Synchronisation?

In Java ist Synchronisierung die Fähigkeit, den Zugriff mehrerer Threads auf jede gemeinsam genutzte Ressource zu steuern. Wenn mehrere Threads versuchen, dieselbe Aufgabe gleichzeitig auszuführen, erhalten Sie möglicherweise ein falsches Ergebnis. Um dieses Problem zu beheben, verwendet Java die Synchronisierung, die die Ausführung jeweils nur eines Threads ermöglicht. Die Synchronisierung kann auf drei Arten erreicht werden:
  • Synchronisieren einer Methode
  • Synchronisieren eines bestimmten Blocks
  • Statische Synchronisierung

Synchronisieren einer Methode

Eine synchronisierte Methode wird verwendet, um ein Objekt für eine beliebige gemeinsam genutzte Ressource zu sperren. Wenn ein Thread eine synchronisierte Methode aufruft, erhält er automatisch die Sperre des Objekts und gibt sie frei, wenn der Thread seine Aufgabe abgeschlossen hat. Damit dies funktioniert, müssen Sie das synchronisierte Schlüsselwort hinzufügen. Wir können anhand eines Beispiels sehen, wie das funktioniert:

/**
* An example where we synchronize a method. That is, we add the synchronized keyword to it.
* There are two authors who want to use one printer. Each of them has composed their own poems
* And of course they don’t want their poems mixed up. Instead, they want work to be performed in * * * order for each of them
*/
class Printer {

   synchronized void print(List<String> wordsToPrint) {
       wordsToPrint.forEach(System.out::print);
       System.out.println();
   }

   public static void main(String args[]) {
       // One object for two threads
       Printer printer  = new Printer();

       // Create two threads
       Writer1 writer1 = new Writer1(printer);
       Writer2 writer2 = new Writer2(printer);

       // Start them
       writer1.start();
       writer2.start();
   }
}

/**
* Author No. 1, who writes an original poem.
*/
class Writer1 extends Thread {
   Printer printer;

   Writer1(Printer printer) {
       this.printer = printer;
   }

   public void run() {
       List<string> poem = Arrays.asList("I ", this.getName(), " Write", " A Letter");
       printer.print(poem);
   }

}

/**
* Author No. 2, who writes an original poem.
*/
class Writer2 extends Thread {
   Printer printer;

   Writer2(Printer printer) {
       this.printer = printer;
   }

   public void run() {
       List<String> poem = Arrays.asList("I Do Not ", this.getName(), " Not Write", " No Letter");
       printer.print(poem);
   }
}
Und die Konsolenausgabe ist diese:
Ich Thread-0 schreibe einen Brief. Ich schreibe nicht Thread-1. Schreibe keinen Brief

Synchronisationsblock

Ein synchronisierter Block kann verwendet werden, um eine Synchronisierung für eine bestimmte Ressource in einer Methode durchzuführen. Nehmen wir an, dass Sie in einer großen Methode (ja, Sie sollten sie nicht schreiben, aber manchmal passieren sie) aus irgendeinem Grund nur einen kleinen Abschnitt synchronisieren müssen. Wenn Sie den gesamten Code der Methode in einen synchronisierten Block einfügen, funktioniert er genauso wie eine synchronisierte Methode. Die Syntax sieht so aus:

synchronized ("object to be locked") {
   // The code that must be protected
}
Um eine Wiederholung des vorherigen Beispiels zu vermeiden, erstellen wir Threads mithilfe anonymer Klassen, dh wir implementieren sofort die Runnable-Schnittstelle.

/**
* This is how a synchronization block is added.
* Inside the block, you need to specify which object's mutex will be acquired.
*/
class Printer {

   void print(List<String> wordsToPrint) {
       synchronized (this) {
           wordsToPrint.forEach(System.out::print);
       }
       System.out.println();
   }

   public static void main(String args[]) {
       // One object for two threads
       Printer printer = new Printer();

       // Create two threads
       Thread writer1 = new Thread(new Runnable() {
           @Override
           public void run() {
               List<String> poem = Arrays.asList("I ", "Writer1", " Write", " A Letter");
               printer.print(poem);
           }
       });
       Thread writer2 = new Thread(new Runnable() {
           @Override
           public void run() {
               List<String> poem = Arrays.asList("I Do Not ", "Writer2", " Not Write", " No Letter");
               printer.print(poem);
           }
       });

       // Start them
       writer1.start();
       writer2.start();
   }
}

}
Und die Konsolenausgabe ist diese:
Ich, Writer1, schreibe einen Brief, ich nicht, Writer2, schreibe keinen Brief

Statische Synchronisierung

Wenn Sie eine statische Methode synchronisieren, erfolgt die Sperrung für die Klasse und nicht für das Objekt. In diesem Beispiel führen wir eine statische Synchronisierung durch, indem wir das synchronisierte Schlüsselwort auf eine statische Methode anwenden:

/**
* This is how a synchronization block is added.
* Inside the block, you need to specify which object's mutex will be acquired.
*/
class Printer {

   static synchronized void print(List<String> wordsToPrint) {
       wordsToPrint.forEach(System.out::print);
       System.out.println();
   }

   public static void main(String args[]) {

       // Create two threads
       Thread writer1 = new Thread(new Runnable() {
           @Override
           public void run() {
               List<String> poem = Arrays.asList("I ", "Writer1", " Write", " A Letter");
               Printer.print(poem);
           }
       });
       Thread writer2 = new Thread(new Runnable() {
           @Override
           public void run() {
               List<String> poem = Arrays.asList("I Do Not ", "Writer2", " Not Write", " No Letter");
               Printer.print(poem);
           }
       });

       // Start them
       writer1.start();
       writer2.start();
   }
}
Und die Konsolenausgabe ist diese:
Ich schreibe keinen Brief. Ich schreibe keinen Brief. Ich schreibe keinen Brief

34. Was ist eine volatile Variable?

Bei der Multithread-Programmierung wird das Schlüsselwort volatile zur Thread-Sicherheit verwendet. Wenn eine veränderbare Variable geändert wird, ist die Änderung für alle anderen Threads sichtbar, sodass eine Variable jeweils von einem Thread verwendet werden kann. Durch die Verwendung des Schlüsselworts volatile können Sie garantieren, dass eine Variable threadsicher ist und im gemeinsam genutzten Speicher gespeichert wird und dass Threads sie nicht in ihren Caches speichern. Wie sieht das aus?

private volatile AtomicInteger count;
Wir fügen der Variablen einfach volatile hinzu. Beachten Sie jedoch, dass dies keine vollständige Thread-Sicherheit bedeutet. Schließlich sind Operationen an der Variablen möglicherweise nicht atomar. Allerdings können Sie Atomic- Klassen verwenden, die Operationen atomar ausführen, also in einem einzigen CPU-Befehl. Es gibt viele solcher Klassen im Paket java.util.concurrent.atomic .

35. Was ist ein Deadlock?

In Java kann es im Rahmen von Multithreading zu Deadlocks kommen. Ein Deadlock kann auftreten, wenn ein Thread auf die Sperre eines Objekts wartet, die von einem anderen Thread erworben wurde, und der zweite Thread auf die Sperre des Objekts wartet, die vom ersten Thread erworben wurde. Das bedeutet, dass die beiden Threads aufeinander warten und die Ausführung ihres Codes nicht fortgesetzt werden kann. Die 50 wichtigsten Fragen und Antworten zu Vorstellungsgesprächen für Java Core.  Teil 2 - 4Betrachten wir ein Beispiel mit einer Klasse, die Runnable implementiert. Sein Konstruktor benötigt zwei Ressourcen. Die run()-Methode erwirbt die Sperre für sie der Reihe nach. Wenn Sie zwei Objekte dieser Klasse erstellen und die Ressourcen in einer anderen Reihenfolge übergeben, kann es leicht zu einem Deadlock kommen:

class DeadLock {

   public static void main(String[] args) {
       final Integer r1 = 10;
       final Integer r2 = 15;

       DeadlockThread threadR1R2 = new DeadlockThread(r1, r2);
       DeadlockThread threadR2R1 = new DeadlockThread(r2, r1);

       new Thread(threadR1R2).start();
       new Thread(threadR2R1).start();
   }
}

/**
* A class that accepts two resources.
*/
class DeadlockThread implements Runnable {

   private final Integer r1;
   private final Integer r2;

   public DeadlockThread(Integer r1, Integer r2) {
       this.r1 = r1;
       this.r2 = r2;
   }

   @Override
   public void run() {
       synchronized (r1) {
           System.out.println(Thread.currentThread().getName() + " acquired resource: " + r1);

           try {
               Thread.sleep(1000);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }

           synchronized (r2) {
               System.out.println(Thread.currentThread().getName() + " acquired resource: " + r2);
           }
       }
   }
}
Konsolenausgabe:
Der erste Thread hat die erste Ressource erworben. Der zweite Thread hat die zweite Ressource erworben

36. Wie vermeidet man Deadlocks?

Da wir wissen, wie ein Deadlock auftritt, können wir einige Schlussfolgerungen ziehen ...
  • Im obigen Beispiel tritt der Deadlock aufgrund der Tatsache auf, dass wir verschachtelte Sperren haben. Das heißt, wir haben einen synchronisierten Block innerhalb eines synchronisierten Blocks. Um dies zu vermeiden, müssen Sie anstelle einer Verschachtelung eine neue höhere Abstraktionsschicht erstellen, die Synchronisierung auf eine höhere Ebene verschieben und die verschachtelte Sperre beseitigen.
  • Je häufiger Sie sperren, desto wahrscheinlicher ist es, dass es zu einem Deadlock kommt. Daher müssen Sie jedes Mal, wenn Sie einen synchronisierten Block hinzufügen, darüber nachdenken, ob Sie ihn wirklich benötigen und ob Sie das Hinzufügen eines neuen Blocks vermeiden können.
  • Verwenden von Thread.join() . Es kann auch zu einem Deadlock kommen, während ein Thread auf einen anderen wartet. Um dieses Problem zu vermeiden, können Sie erwägen, eine Zeitüberschreitung für die Methode „join()“ festzulegen .
  • Wenn wir einen Thread haben, wird es keinen Deadlock geben ;)

37. Was ist eine Rennbedingung?

Wenn es bei realen Rennen um Autos geht, dann bei Rennen im Multithreading um Threads. Aber warum? :/ Es laufen zwei Threads, die auf dasselbe Objekt zugreifen können. Und sie versuchen möglicherweise gleichzeitig, den Status des gemeinsam genutzten Objekts zu aktualisieren. Bisher ist alles klar, oder? Threads werden entweder buchstäblich parallel (wenn der Prozessor über mehr als einen Kern verfügt) oder sequentiell ausgeführt, wobei der Prozessor verschachtelte Zeitscheiben zuweist. Wir können diese Prozesse nicht bewältigen. Das heißt, wenn ein Thread Daten von einem Objekt liest, können wir nicht garantieren, dass er Zeit hat, das Objekt zu ändern, BEVOR ein anderer Thread dies tut. Solche Probleme entstehen, wenn wir diese „Check-and-Act“-Kombinationen haben. Was bedeutet das? Angenommen, wir haben eine if- Anweisung, deren Körper die if-Bedingung selbst ändert, zum Beispiel:

int z = 0;

// Check
if (z < 5) {
// Act
   z = z + 5;
}
Zwei Threads könnten gleichzeitig in diesen Codeblock eintreten, wenn z noch Null ist, und dann könnten beide Threads seinen Wert ändern. Dadurch erhalten wir nicht den erwarteten Wert von 5, sondern 10. Wie vermeidet man das? Sie müssen eine Sperre erwerben, bevor Sie prüfen und handeln, und die Sperre anschließend aufheben. Das heißt, Sie müssen den ersten Thread veranlassen, den if- Block einzugeben, alle Aktionen auszuführen, z zu ändern und erst dann dem nächsten Thread die Möglichkeit zu geben, dasselbe zu tun. Aber der nächste Thread wird den if- Block nicht betreten, da z jetzt 5 ist:

// Acquire the lock for z
if (z < 5) {
   z = z + 5;
}
// Release z's lock
===================================================

Statt einer Schlussfolgerung

Ich möchte mich bei allen bedanken, die bis zum Ende gelesen haben. Es war ein langer Weg, aber du hast es durchgehalten! Vielleicht ist nicht alles klar. Das ist normal. Als ich anfing, Java zu studieren, konnte ich mir nicht vorstellen, was eine statische Variable ist. Aber keine große Sache. Ich habe darüber geschlafen, noch ein paar Quellen gelesen und dann kam die Erkenntnis. Die Vorbereitung auf ein Vorstellungsgespräch ist eher eine akademische als eine praktische Frage. Daher sollten Sie vor jedem Vorstellungsgespräch die Dinge, die Sie möglicherweise nicht oft verwenden, noch einmal durchgehen und in Ihrem Gedächtnis auffrischen.

Und wie immer gibt es hier einige nützliche Links:

Vielen Dank fürs Lesen. Bis bald :) Mein GitHub-ProfilDie 50 wichtigsten Fragen und Antworten zu Vorstellungsgesprächen für Java Core.  Teil 2 - 5
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION