Heim  >  Artikel  >  Java  >  Häufige Java-Multithreading-Interviewfragen

Häufige Java-Multithreading-Interviewfragen

(*-*)浩
(*-*)浩Original
2019-12-24 15:02:402373Durchsuche

Häufige Java-Multithreading-Interviewfragen

Was ist der Unterschied zwischen Parallelität und Parallelität? (Empfohlenes Lernen: Häufige Java-Testfragen )

Bestehen bedeutet, dass zwei oder mehr Ereignisse gleichzeitig auftreten. Parallelität bedeutet, dass zwei oder mehr Ereignisse gleichzeitig stattfinden Intervall auftritt.

Parallelität bedeutet mehrere Ereignisse auf verschiedenen Entitäten, und Parallelität bedeutet mehrere Ereignisse auf derselben Entität.

Verarbeiten Sie mehrere Aufgaben „gleichzeitig“ auf einem Prozessor und mehrere Aufgaben gleichzeitig auf mehreren Prozessoren. Zum Beispiel ein verteilter Hadoop-Cluster.

Das Ziel der gleichzeitigen Programmierung besteht also darin, jeden Kern des Prozessors voll auszunutzen, um die höchste Verarbeitungsleistung zu erzielen.

Was ist der Unterschied zwischen Thread und Prozess?

Kurz gesagt ist ein Prozess die Grundeinheit der Programmausführung und Ressourcenzuweisung. Ein Programm hat mindestens einen Prozess und ein Prozess hat mindestens einen Thread. Der Prozess verfügt während der Ausführung über eine unabhängige Speichereinheit, und mehrere Threads teilen sich Speicherressourcen, wodurch die Anzahl der Umschaltzeiten reduziert und die Effizienz gesteigert wird.

Ein Thread ist eine Entität eines Prozesses, die Grundeinheit der CPU-Planung und -Verteilung und eine Grundeinheit, die kleiner als ein Programm ist und unabhängig ausgeführt werden kann. Mehrere Threads im selben Prozess können gleichzeitig ausgeführt werden.

Was ist ein Daemon-Thread?

Der Daemon-Thread ist ein Service-Thread. Genauer gesagt bedient er andere Threads.

Wie kann ich einen Thread erstellen?

①.Erben Sie die Thread-Klasse, um eine Thread-Klasse zu erstellen.

Definieren Sie eine Unterklasse der Thread-Klasse und überschreiben Sie den Methodenkörper der Ausführungsmethode die Thread-Anforderungen Abgeschlossene Aufgaben. Daher wird die run()-Methode als Ausführungskörper bezeichnet.

Erstellen Sie eine Instanz der Thread-Unterklasse, dh erstellen Sie ein Thread-Objekt.

Rufen Sie die start()-Methode des Thread-Objekts auf, um den Thread zu starten.

②. Erstellen Sie eine Thread-Klasse über die ausführbare Schnittstelle

Definieren Sie die Implementierungsklasse der ausführbaren Schnittstelle und überschreiben Sie die run()-Methode der Schnittstelle. Die Methode ist auch der Thread-Ausführungskörper.

Erstellen Sie eine Instanz der Runnable-Implementierungsklasse und verwenden Sie diese Instanz als Ziel von Thread, um ein Thread-Objekt zu erstellen. Dieses Thread-Objekt ist das eigentliche Thread-Objekt.

Rufen Sie die start()-Methode des Thread-Objekts auf, um den Thread zu starten.

③. Erstellen Sie Threads über Callable und Future

Erstellen Sie eine Implementierungsklasse der Callable-Schnittstelle und implementieren Sie die call()-Methode, die als Thread-Ausführungskörper dient und einen Rückgabewert hat.

Erstellen Sie eine Instanz der Callable-Implementierungsklasse und verwenden Sie die FutureTask-Klasse, um das Callable-Objekt zu umschließen. Das FutureTask-Objekt kapselt den Rückgabewert der call()-Methode des Callable-Objekts.

Verwenden Sie das FutureTask-Objekt als Ziel des Thread-Objekts, um einen neuen Thread zu erstellen und zu starten.

Rufen Sie die get()-Methode des FutureTask-Objekts auf, um den Rückgabewert zu erhalten, nachdem die Ausführung des Sub-Threads beendet ist.

Sagen Sie mir, was der Unterschied zwischen runnable und callable ist?

Dies ist eine ziemlich tiefgründige Frage, und sie zeigt auch die Breite des Wissens, das ein Java-Programmierer erwerben kann.

Der Rückgabewert der run()-Methode in der Runnable-Schnittstelle ist ungültig und führt lediglich den Code in der run()-Methode aus.

Der Aufruf( in der Die Methode Callable interface hat einen Rückgabewert und ist ein generischer Typ. Sie kann verwendet werden, um die Ergebnisse der asynchronen Ausführung in Verbindung mit Future und FutureTask zu erhalten.

Welchen Status haben Threads?

Threads haben normalerweise fünf Zustände: erstellt, bereit, ausgeführt, blockiert und tot.

Status erstellen. Wenn das Thread-Objekt generiert wird, wird die Startmethode des Objekts nicht aufgerufen, was bedeutet, dass sich der Thread im Erstellungsstatus befindet.

Bereitschaftsstatus. Wenn die Startmethode des Thread-Objekts aufgerufen wird, wechselt der Thread in den Bereitschaftszustand. Zu diesem Zeitpunkt hat der Thread-Scheduler den Thread jedoch nicht als aktuellen Thread festgelegt und befindet sich zu diesem Zeitpunkt im Bereitschaftszustand. Nachdem der Thread ausgeführt wurde, befindet er sich auch nach der Rückkehr aus dem Warten oder Schlafen im Bereitschaftszustand.

Laufstatus. Der Thread-Scheduler versetzt den Thread in den Bereitschaftszustand als aktuellen Thread. Zu diesem Zeitpunkt wechselt der Thread in den Ausführungszustand und beginnt mit der Ausführung des Codes in der Ausführungsfunktion.

Blockierungsstatus. Wenn ein Thread ausgeführt wird, wird er angehalten, normalerweise um eine bestimmte Zeit abzuwarten (z. B. wenn eine bestimmte Ressource bereit ist), bevor die Ausführung fortgesetzt wird. Sleep, Suspend, Wait und andere Methoden können zum Blockieren von Threads führen.

Todeszustand. Wenn die Ausführungsmethode eines Threads endet oder die Stoppmethode aufgerufen wird, stirbt der Thread. Für einen Thread, der gestorben ist, können Sie die Startmethode nicht mehr verwenden, um ihn vorzubereiten.

Was ist der Unterschied zwischen sleep() und wait()?

sleep(): Die Methode ist eine statische Methode der Thread-Klasse (Thread), die den aufrufenden Thread in den Ruhezustand versetzt und anderen Threads Ausführungsmöglichkeiten gibt, nachdem die Ruhezeit abgelaufen ist. Der Thread wechselt in den Bereitschaftszustand und andere Threads konkurrieren miteinander um die CPU-Ausführungszeit.

Da es sich bei „sleep()“ um eine statische Methode handelt, kann sie die Maschinensperre des Objekts nicht ändern. Wenn die Methode „sleep()“ in einem synchronisierten Block aufgerufen wird, wird die Maschinensperre des Objekts nicht aufgehoben, obwohl der Thread in den Ruhezustand wechselt . Andere Threads können immer noch nicht auf dieses Objekt zugreifen.

wait(): wait() ist eine Methode der Object-Klasse. Wenn ein Thread die Wartemethode ausführt, tritt er in einen Wartepool ein, der sich auf das Objekt bezieht, und gibt die Maschinensperre des Objekts frei, damit andere Threads darauf zugreifen können. Sie können wartende Threads über die Methoden notify und notifyAll wecken

Was ist der Unterschied zwischen notify() und notifyAll()?

Wenn ein Thread die Methode wait() eines Objekts aufruft, befindet sich der Thread im Wartepool des Objekts und die Threads im Wartepool konkurrieren nicht um die Sperre des Objekts.

Wenn ein Thread die Methode notifyAll() des Objekts (weckt alle Warte-Threads auf) oder die Methode notify() (weckt zufällig nur einen Warte-Thread auf) aufruft, gelangt der aufgeweckte Thread in den Sperrpool des Objekts, die Threads im Sperrpool konkurrieren um die Objektsperre.

Das heißt, nach dem Aufruf von notify verschiebt notifyAll alle Threads im Objektwartepool in den Sperrpool, um auf den Sperrenwettbewerb zu warten, solange ein Thread aus dem Wartepool in den Sperrpool eintritt.

Threads mit hoher Priorität konkurrieren mit hoher Wahrscheinlichkeit um die Objektsperre. Wenn ein Thread nicht um die Objektsperre konkurriert, bleibt er nur dann im Sperrpool, wenn der Thread die Methode wait() aufruft Wieder kann es in den Wartepool zurückkehren.

Der Thread, der um die Objektsperre konkurriert, wird so lange ausgeführt, bis der synchronisierte Codeblock ausgeführt wird, und er gibt die Objektsperre frei. Zu diesem Zeitpunkt konkurrieren die Threads im Sperrpool weiterhin um die Objektsperre.

Was ist der Unterschied zwischen Thread run() und start()?

Jeder Thread schließt seine Operation über die Methode run() ab, die einem bestimmten Thread-Objekt entspricht. Die Methode run() wird als Thread-Körper bezeichnet. Starten Sie einen Thread, indem Sie die start()-Methode der Thread-Klasse aufrufen.

start()-Methode zum Starten eines Threads, wodurch ein Multithread-Betrieb wirklich realisiert wird. Zu diesem Zeitpunkt müssen Sie nicht auf die Ausführung des Hauptteilcodes der Ausführungsmethode warten. Sie können den folgenden Code zu diesem Zeitpunkt direkt ausführen. Der Thread befindet sich im Bereitschaftszustand und wird nicht ausgeführt.

Rufen Sie dann die Methode run() über diese Thread-Klasse auf, um ihren Ausführungsstatus abzuschließen. Die Methode run() wird hier als Thread-Körper bezeichnet, der den Inhalt des auszuführenden Threads enthält , und diese Thread-Beendigung. Die CPU plant dann andere Threads.

Die run()-Methode befindet sich in diesem Thread. Es handelt sich nur um eine Funktion im Thread, nicht um mehrere Threads. Wenn Sie run() direkt aufrufen, entspricht dies tatsächlich dem Aufruf einer normalen Funktion. Wenn Sie die run()-Methode direkt verwenden, müssen Sie warten, bis die run()-Methode die Ausführung abgeschlossen hat, bevor Sie den folgenden Code ausführen immer noch nur ein Ausführungspfad und es gibt überhaupt keine Thread-Eigenschaften, daher sollte bei der Multi-Thread-Ausführung die start()-Methode anstelle der run()-Methode verwendet werden.

Wie kann ein Thread-Pool erstellt werden?

①. newFixedThreadPool(int nThreads)

Erstellen Sie einen Thread-Pool mit fester Länge und erstellen Sie bei jeder Übermittlung einer Aufgabe einen Thread, bis die maximale Anzahl von Thread-Pools erreicht ist Dieses Mal ändert sich die Thread-Größe nicht mehr. Wenn ein Thread aufgrund eines unerwarteten Fehlers endet, fügt der Thread-Pool einen neuen Thread hinzu.

②.newCachedThreadPool()

Erstellen Sie einen zwischenspeicherbaren Thread-Pool, wenn die Nachfrage steigt automatisch recycelt. Beim Hinzufügen neuer Threads gibt es keine Begrenzung für die Größe des Thread-Pools.

③. newSingleThreadExecutor()

Dies ist ein Single-Threaded-Executor, der einen einzelnen Arbeitsthread zum Ausführen von Aufgaben erstellt. Wenn dieser Thread abnormal endet, wird ein neuer erstellt, um ihn zu ersetzen ; Das Merkmal besteht darin, sicherzustellen, dass Aufgaben seriell gemäß der Reihenfolge in der Warteschlange ausgeführt werden.

④. newScheduledThreadPool(int corePoolSize)

Erstellt einen Thread-Pool fester Länge und führt Aufgaben verzögert oder zeitgesteuert aus, ähnlich wie bei Timer.

Welche Status hat der Thread-Pool?

Der Thread-Pool hat 5 Zustände: Läuft, Herunterfahren, Stopp, Aufräumen, Beendet.

Rahmendiagramm jedes Zustandswechsels im Thread-Pool:

Häufige Java-Multithreading-Interviewfragen

Die Methoden „submit()“ und „execute()“. Im Thread-Pool sind Was ist der Unterschied?

Die empfangenen Parameter sind unterschiedlich

Submit hat einen Rückgabewert, Execute jedoch nicht. Wie kann die Sicherheit des Multithread-Betriebs gewährleistet werden?

Thread-Sicherheit spiegelt sich in drei Aspekten wider:

Atomizität: Bereitstellung eines sich gegenseitig ausschließenden Zugriffs, nur ein Thread kann gleichzeitig Daten bearbeiten (atomar). , synchronisiert);

Sichtbarkeit: Änderungen am Hauptspeicher durch einen Thread können von anderen Threads rechtzeitig gesehen werden, (synchronisiert, flüchtig);

Ordnung: Ein Thread beobachtet andere Threads. Die Reihenfolge von Die Ausführung von Anweisungen in einem Thread ist aufgrund der Neuordnung der Anweisungen im Allgemeinen unorganisiert (passiert vorher).

Was ist das Upgrade-Prinzip der Multithread-Sperre?

In Java gibt es vier Sperrzustände: zustandslose Sperre, voreingenommene Sperre, leichter Sperrzustand und schwerer Sperrzustand eskaliert. Schlösser können hochgestuft, aber nicht herabgestuft werden.

Illustrierter Prozess der Schlossaktualisierung:

Häufige Java-Multithreading-Interviewfragen

Was ist ein Deadlock?

Deadlock bezieht sich auf ein Blockierungsphänomen, das dadurch verursacht wird, dass zwei oder mehr Prozesse während der Ausführung um Ressourcen konkurrieren oder miteinander kommunizieren. Ohne äußere Kraft können sie nicht fortfahren. Zu diesem Zeitpunkt befindet sich das System in einem Deadlock-Zustand oder das System hat einen Deadlock. Diese Prozesse, die immer aufeinander warten, werden Deadlock-Prozesse genannt.

ist ein Fehler auf Betriebssystemebene und die Abkürzung für Prozess-Deadlock. Er wurde erstmals 1965 von Dijkstra vorgeschlagen, als er den Banker-Algorithmus untersuchte. Er ist der am schwierigsten zu handhabende Fehler in Computer-Betriebssystemen im gesamten Bereich der gleichzeitigen Programmierung Eine der Fragen.

Wie kann ein Deadlock verhindert werden?

Vier notwendige Bedingungen für einen Deadlock:

Bedingung zum gegenseitigen Ausschluss: Ein Prozess erlaubt anderen Prozessen nicht, auf die zugewiesenen Ressourcen zuzugreifen Ressource, Sie können nur warten, bis der Prozess, der die Ressource belegt, seine Nutzung abschließt und die Ressource freigibt

Anforderungs- und Aufbewahrungsbedingungen: Nachdem der Prozess eine bestimmte Ressource erhalten hat, stellt er eine Anforderung für andere Ressourcen, die Ressource kann jedoch möglicherweise verwendet werden Von anderen Prozessen belegt sein, wird diese Anforderung blockiert, behält aber die erhaltenen Ressourcen bei

Nicht entzugsfähige Bedingung: Bezieht sich auf die vom Prozess erhaltene Ressource, die nicht entzogen werden kann, bevor die Nutzung abgeschlossen ist. Es kann nur verwendet werden, nachdem es nach der Verwendung freigegeben wurde

Schleifenwartebedingungen: Bezieht sich auf die vier Bedingungen, nachdem ein Prozess-Deadlock auftritt und mehrere Prozesse eine Kopf-an-Ende-Schleife bilden, die auf eine Ressourcenbeziehung wartet

sind notwendige Bedingungen für einen Deadlock. Solange ein Deadlock im System auftritt, müssen diese Bedingungen erfüllt sein.

Wenn Sie die Ursachen von Deadlocks verstehen, insbesondere die vier notwendigen Bedingungen für Deadlocks, können Sie Deadlocks weitestgehend vermeiden, verhindern und beseitigen.

Achten Sie daher im Hinblick auf Systemdesign, Prozessplanung usw. darauf, wie Sie verhindern können, dass diese vier notwendigen Bedingungen geschaffen werden, und wie Sie einen angemessenen Ressourcenzuweisungsalgorithmus festlegen, um zu vermeiden, dass Prozesse dauerhaft Systemressourcen belegen .

Darüber hinaus muss verhindert werden, dass Prozesse im Wartezustand Ressourcen belegen. Daher muss die Ressourcenallokation richtig geplant werden.

Was ist ThreadLocal? Welche Nutzungsszenarien gibt es?

Lokale Thread-Variablen sind auf den Thread beschränkte Variablen. Sie gehören zum Thread selbst und werden nicht von mehreren Threads gemeinsam genutzt. Java stellt die ThreadLocal-Klasse zur Unterstützung lokaler Thread-Variablen bereit, wodurch Thread-Sicherheit erreicht werden kann.

Aber seien Sie besonders vorsichtig, wenn Sie Thread-lokale Variablen in einer verwalteten Umgebung (z. B. einem Webserver) verwenden, in der der Lebenszyklus des Arbeitsthreads länger ist als der Lebenszyklus aller Anwendungsvariablen.

Sobald eine Thread-lokale Variable nach Abschluss der Arbeit nicht freigegeben wird, besteht bei Java-Anwendungen die Gefahr von Speicherlecks.

Erzählen Sie mir etwas über das zugrunde liegende Implementierungsprinzip der Synchronisierung?

Synchronized kann sicherstellen, dass beim Ausführen einer Methode oder eines Codeblocks jeweils nur eine Methode gleichzeitig in den kritischen Abschnitt gelangen kann, und es kann auch die Speichersichtbarkeit gemeinsam genutzter Variablen sicherstellen.

Jedes Objekt in Java kann als Sperre verwendet werden, was die Grundlage für die synchronisierte Implementierung der Synchronisierung ist:

Gemeinsame Synchronisationsmethode, die Sperre ist das aktuelle Instanzobjekt

Statische Synchronisationsmethode, Sperre Es ist das Klassenobjekt der aktuellen Klasse

synchronisierter Methodenblock, und die Sperre ist das Objekt in Klammern

Was ist der Unterschied zwischen synchronisiert und flüchtig? ?

Der Kern von volatile besteht darin, dem JVM mitzuteilen, dass der Wert der aktuellen Variablen im Register (Arbeitsspeicher) unsicher ist und aus dem Hauptspeicher gelesen werden muss, um die aktuelle Variable zu sperren Nur der aktuelle Thread kann auf diese Variable zugreifen, andere Threads werden blockiert.

flüchtig kann nur auf Variablenebene verwendet werden; synchronisiert kann auf Variablen-, Methoden- und Klassenebene verwendet werden.

flüchtig kann nur die Änderungssichtbarkeit von Variablen realisieren und kann keine Atomizität garantieren, während synchronisiert die Änderungssichtbarkeit und Atomizität von Variablen garantieren kann.

Volatile verursacht keine Thread-Blockierung; synchronisiert kann eine Thread-Blockierung verursachen.

Variablen, die als flüchtig gekennzeichnet sind, werden vom Compiler nicht optimiert. Variablen, die als synchronisiert markiert sind, können vom Compiler optimiert werden.

Was ist der Unterschied zwischen synchronisiert und gesperrt?

Synchronized ist ein in Java integriertes Schlüsselwort. Auf JVM-Ebene ist Lock eine Java-Klasse.

Synchronized kann nicht feststellen, ob der Sperrstatus vorliegt. aber Lock kann feststellen, ob die Sperre erworben wurde;

synchronized gibt die Sperre automatisch frei (ein Thread gibt die Sperre frei, nachdem der Synchronisationscode ausgeführt wurde; ein b-Thread gibt die Sperre frei, wenn während der Ausführung eine Ausnahme auftritt), Lock muss manuell freigegeben werden (die Methode unlock() hebt die Sperre auf). Andernfalls kann es leicht zu einem Thread-Deadlock kommen.

Verwenden Sie das synchronisierte Schlüsselwort für zwei Threads 1 und 2. Wenn der aktuelle Thread 1 Erhält die Sperre, Thread 2 wartet. Wenn Thread 1 blockiert ist, wartet Thread 2 ewig und die Sperre muss nicht unbedingt warten. Wenn die Sperre nicht erhalten werden kann, kann der Thread ohne Wartezeit beendet werden.

Die synchronisierte Sperre kann wiederholt werden und unfair, während Lock-Sperren reentrant, beurteilbar und fair sind (beides ist möglich);

Lock-Sperren eignen sich für Synchronisationsprobleme mit einer großen Menge synchronisierten Codes, und synchronisierte Sperren eignen sich für kleine Mengen Code. Synchronisierungsprobleme.

Was ist der Unterschied zwischen synchronisiert und ReentrantLock?

synchronized ist das gleiche Schlüsselwort wie if, else, for und while, und ReentrantLock ist eine Klasse. Dies ist der wesentliche Unterschied zwischen den beiden.

Da ReentrantLock eine Klasse ist, bietet sie mehr und flexiblere Funktionen als synchronisiert. Sie kann vererbt werden, kann über verschiedene Klassenvariablen verfügen und ist in mehreren Punkten erweiterbarer 🎜>

ReentrantLock kann die Wartezeit für den Erwerb von Sperren festlegen und so Deadlocks vermeiden

ReentrantLock kann Informationen über verschiedene Sperren abrufen

ReentrantLock kann mehrere Benachrichtigungen flexibel implementieren

Darüber hinaus sind die Sperrmechanismen der beiden tatsächlich unterschiedlich: ReentrantLock ruft die Park-Methode von Unsafe auf, um unten zu sperren, während synchronisiert das Markierungswort im Objektheader bearbeiten sollte.

Erzählen Sie mir etwas über das Prinzip der Atomkraft?

Das Grundmerkmal der Klassen im Atomic-Paket besteht darin, dass in einer Multithread-Umgebung, wenn mehrere Threads gleichzeitig eine einzelne Variable (einschließlich Basistypen und Referenztypen) bearbeiten, sie sind exklusiv, das heißt, wenn mehrere Threads gleichzeitig den Wert dieser Variablen aktualisieren, kann nur ein Thread erfolgreich sein, und der erfolglose Thread kann es weiterhin wie eine Spin-Sperre versuchen, bis die Ausführung erfolgreich ist.

Die Kernmethoden in der Atomic-Klassenreihe rufen mehrere lokale Methoden in der unsicheren Klasse auf. Eine Sache, die wir zuerst wissen müssen, ist die Unsafe-Klasse, deren vollständiger Name lautet: sun.misc.Unsafe. Diese Klasse enthält eine große Anzahl von Operationen für C-Code, einschließlich vieler direkter Speicherzuweisungen und Aufrufe von atomaren Operationen als nicht sicher markiert ist, sage ich Ihnen, dass eine große Anzahl von Methodenaufrufen darin Sicherheitsrisiken birgt und Sie diese mit Vorsicht verwenden müssen, da dies sonst zu schwerwiegenden Konsequenzen führen kann. Wenn beispielsweise Speicher über unsicher zugewiesen wird, Wenn Sie bestimmte Bereiche angeben, kann es zu ähnlichen Problemen wie bei C++ kommen. Der Zeiger überschreitet die Grenze zu anderen Prozessen.

Das obige ist der detaillierte Inhalt vonHäufige Java-Multithreading-Interviewfragen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn