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.
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.
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.
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
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: 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
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:
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!