Heim  >  Artikel  >  Betrieb und Instandhaltung  >  So analysieren Sie die Doppelprüfungssperre in der JAVA-Sprache

So analysieren Sie die Doppelprüfungssperre in der JAVA-Sprache

王林
王林nach vorne
2023-05-12 08:55:171169Durchsuche

1. Doppelt überprüftes Sperren

Bei der Programmentwicklung ist es manchmal erforderlich, einige teure Objektinitialisierungsvorgänge zu verschieben und sie nur dann zu initialisieren, wenn diese Objekte verwendet werden. In diesem Fall kann ein doppelt überprüftes Sperren verwendet werden, um Objekte zu verzögern Initialisierungsoperationen. Bei der Doppelprüfungssperre handelt es sich um ein Software-Entwurfsmuster, das den Wettbewerbs- und Synchronisierungsaufwand in gleichzeitigen Systemen reduzieren soll. Basierend auf dem gewöhnlichen Singleton-Muster wird zunächst festgestellt, ob das Objekt initialisiert wurde, und dann wird entschieden, ob es gesperrt wird. Obwohl das doppelt überprüfte Sperren die fehleranfälligen und Thread-unsicheren Probleme gewöhnlicher Singleton-Muster in Multithread-Umgebungen löst, gibt es dennoch einige versteckte Gefahren. Im Folgenden wird der Quellcode der JAVA-Sprache als Beispiel verwendet, um die Ursachen und Reparaturmethoden für Doppelprüfungs-Sperrfehler zu analysieren.

2. Die Gefahren der Double-Check-Sperre

Double-Check-Sperre hat in einer Single-Thread-Umgebung keine Auswirkungen, da Threads in diesem Fall jederzeit die Ausführung wechseln Bei der Neuanordnung von Anweisungen ist die Instanziierung des Objekts nicht vollständig, was zu einem Fehler beim Programmaufruf führt.

3. Beispielcode

Das Beispiel stammt aus der Samate Juliet Test Suite für Java v1.3 (https://samate.nist.gov/SARD/testsuite.php), Quelldateiname: CWE609_Double_Checked_Locking__Servlet_01.java.

3.1 Fehlercode

So analysieren Sie die Doppelprüfungssperre in der JAVA-Sprache

Die obigen Codezeilen sind die Zeilen 23-38. Das Programm ermittelt zunächst, ob stringBad null ist, wenn nicht, dann geben Sie das String-Objekt direkt zurück, wodurch die Notwendigkeit vermieden wird, das synchronisierte verbrauchte Ressourcen blockieren. Wenn <code class="prettyprint code-in-text Prettyprinted">stringBad null ist, verwenden Sie das Schlüsselwort synchronized in einem Multithread Umgebung Vermeiden Sie die mehrfache Erstellung von String-Objekten. Wenn der Code tatsächlich ausgeführt wird, können im obigen Code immer noch Fehler auftreten. stringBad 是否为 null,如果不是则直接返回该 String 对象,这样避免了进入 synchronized 块所需要花费的资源。当 stringBad 为 null 时,使用 synchronized 关键字在多线程环境中避免多次创建 String 对象。在代码实际运行时,以上代码仍然可能发生错误。

对于第33行,创建 stringBad 对象和赋值操作是分两步执行的。但 JVM 不保证这两个操作的先后顺序。当指令重排序后,JVM 会先赋值指向了内存地址,然后再初始化 stringBad 对象。如果此时存在两个线程,两个线程同时进入了第27行。线程1首先进入了 synchronized 块,由于 stringBad 为 null,所以它执行了第33行。当 JVM 对指令进行了重排序,JVM 先分配了实例的空白内存,并赋值给 stringBad,但这时 stringBad 对象还未实例化,然后线程1离开了 synchronized 块。当线程2进入 synchronized 块时,由于 stringBad 此时不是 null ,直接返回了未被实例化的对象(仅有内存地址值,对象实际未初始化)。后续线程2调用程序对 stringBad

Für Zeile 33 werden die Erstellung des stringBad-Objekts und die Zuweisungsoperation in zwei Schritten durchgeführt. Die JVM garantiert jedoch nicht die Reihenfolge dieser beiden Vorgänge. Wenn die Anweisungen neu angeordnet werden, weist die JVM zunächst den Wert zu, der auf die Speicheradresse zeigt, und initialisiert dann das Objekt stringBad. Wenn zu diesem Zeitpunkt zwei Threads vorhanden sind, betreten beide Threads gleichzeitig Zeile 27. Thread 1 hat zuerst den synchronisierten-Block eingegeben, da stringBad null ist Es führt Zeile 33 aus. Wenn die JVM die Anweisungen neu anordnet, weist die JVM zunächst den leeren Speicher der Instanz zu und weist ihn stringBad zu, aber zu diesem Zeitpunkt stringBad-Objekt wurde nicht instanziiert, und dann hat Thread 1 den synchronisierten-Block verlassen. Wenn Thread 2 in den synchronisierten-Block eintritt, weil stringBad nicht null ist Dieses Mal wird das nicht instanziierte Objekt direkt zurückgegeben (nur der Speicheradressenwert, das Objekt wird nicht tatsächlich initialisiert). Wenn nachfolgender Thread 2 das Programm aufruft, um das Objekt stringBad zu bearbeiten, wurde das Objekt zu diesem Zeitpunkt noch nicht initialisiert, sodass ein Fehler auftritt.

Verwenden Sie 360 ​​​​Code Guard, um den oben genannten Beispielcode zu erkennen. Sie können den Fehler „Double Check Lock“ erkennen und die Anzeigeebene ist mittel. Melden Sie den Defekt in Zeile 27 des Codes, wie in Abbildung 1 dargestellt:


So analysieren Sie die Doppelprüfungssperre in der JAVA-Sprache

Abbildung 1: Erkennungsbeispiel für „Double Check Lock“

3.2 Reparaturcode

So analysieren Sie die Doppelprüfungssperre in der JAVA-Sprache

🎜

Verwenden Sie im obigen Reparaturcode das Schlüsselwort volatile in Zeile 23, um die Singleton-Variable stringBad zur Änderung. volatile als Anweisungsschlüsselwort stellt sicher, dass die Anweisung aufgrund der Compileroptimierung nicht weggelassen wird und erfordert jedes Mal ein direktes Lesen des Werts. volatile 关键字来对单例变量 stringBad 进行修饰。 volatile 作为指令关键字确保指令不会因编译器的优化而省略,且要求每次直接读值。

由于编译器优化,代码在实际执行的时候可能与我们编写的顺序不同。编译器只保证程序执行结果与源代码相同,却不保证实际指令的顺序与源代码相同,在单线程环境中并不会出错,然而一旦引入多线程环境,这种乱序就可能导致严重问题。 volatile 关键字就可以从语义上解决这个问题,值得关注的是 volatile 的禁止指令重排序优化功能在 Java 1.5 后才得以实现,因此1.5 前的版本仍然是不安全的,即使使用了 volatile

Aufgrund der Compileroptimierung kann die tatsächliche Ausführung des Codes von der von uns geschriebenen Reihenfolge abweichen. Der Compiler garantiert nur, dass das Ergebnis der Programmausführung mit dem Quellcode übereinstimmt, garantiert jedoch nicht, dass die Reihenfolge der tatsächlichen Anweisungen mit dem Quellcode übereinstimmt. In einer Single-Thread-Umgebung wird jedoch kein Fehler auftreten. Sobald eine Multithread-Umgebung eingeführt wird, kann diese Art von Störung zu ernsthaften Problemen führen. Das Schlüsselwort volatile kann dieses Problem semantisch lösen. Es lohnt sich, darauf zu achten: volatile wurde nach Java 1.5 implementiert, sodass Versionen vor 1.5 immer noch unsicher sind, selbst wenn <code class="prettyprint code-in-text Prettyprinted">flüchtige Schlüsselwörter sind.

Verwenden Sie 360 ​​​​Code Guard, um den reparierten Code zu erkennen, und Sie können sehen, dass der Fehler „Double Check Lock“ nicht mehr besteht. Wie in Abbildung 2 dargestellt:


So analysieren Sie die Doppelprüfungssperre in der JAVA-Sprache

Abbildung 2: Erkennungsergebnisse nach der Reparatur

4 , wie Doppelcheck-Sperren vermeiden

Um Doppelcheck-Sperren zu vermeiden, müssen Sie die folgenden Punkte beachten:

(1) Verwenden Sie das Schlüsselwort volatile, um Vermeiden Sie die Neuordnung von Anweisungen, aber diese Lösung erfordert JDK5 oder höher, da ab JDK5 die neue JSR-133-Speichermodellspezifikation verwendet wird, die die Semantik von Volatile verbessert.

(2) Lösung basierend auf der Klasseninitialisierung.

So analysieren Sie die Doppelprüfungssperre in der JAVA-Sprache
Die JVM führt die Initialisierung der Klasse während der Initialisierungsphase der Klasse durch (dh nachdem die Klasse geladen wurde und bevor sie vom Thread verwendet wird). Während der Initialisierung der Ausführungsklasse erhält die JVM eine Sperre. Diese Sperre kann die Initialisierung derselben Klasse durch mehrere Threads synchronisieren.

#🎜🎜#

Das obige ist der detaillierte Inhalt vonSo analysieren Sie die Doppelprüfungssperre in der JAVA-Sprache. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:yisu.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen