{"id":48849,"date":"2025-05-08T14:48:20","date_gmt":"2025-05-08T12:48:20","guid":{"rendered":"https:\/\/www.happycoders.eu\/?p=48849"},"modified":"2025-12-04T09:36:55","modified_gmt":"2025-12-04T08:36:55","slug":"double-checked-locking","status":"publish","type":"post","link":"https:\/\/www.happycoders.eu\/de\/java\/double-checked-locking\/","title":{"rendered":"Double-Checked Locking in Java"},"content":{"rendered":"\n<p><em>Double-Checked Locking<\/em> ist ein Pattern, um in Multithreading-Umgebungen Objekte lazily (also beim ersten Zugriff darauf) zu initialisieren, ohne dass es dabei zu subtilen Race Conditions kommen kann \u2013 und das ohne den Zugriff auf dieses Objekt vollst\u00e4ndig (und damit zeitaufw\u00e4ndig) zu synchronisieren.<\/p>\n\n\n\n<p class=\"hc-checked-list\">In diesem Artikel erf\u00e4hrst du:<\/p>\n\n\n\n<ul class=\"wp-block-list hc-checked-list\">\n<li>Was ist die Motivation f\u00fcr das Double-Checked Locking?<\/li>\n\n\n\n<li>Welche subtilen Fehler k\u00f6nnen bei der \u201eLazy Initialization\u201c gemacht werden?<\/li>\n\n\n\n<li>Warum ist die vollst\u00e4ndige Synchronisierung mit \u201esynchronized\u201c nicht optimal?<\/li>\n\n\n\n<li>Warum ist das originale Double-Checked Locking Idiom fehlerhaft?<\/li>\n\n\n\n<li>Wie wird Double-Checked Locking in Java korrekt implementiert?<\/li>\n\n\n\n<li>Welche Alternativen gibt es?<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"motivation\">Motivation<\/h2>\n\n\n\n<p>Gelegentlich m\u00f6chten wir ein Objekt erst dann initialisieren, wenn es ben\u00f6tigt wird, da die Initialisierung aufw\u00e4ndig ist und wir den Programmstart nicht unn\u00f6tig verz\u00f6gern wollen.<\/p>\n\n\n\n<p>In single-threaded Anwendungen ist das einfach. Beispielsweise k\u00f6nnten wir das Laden von Einstellungen aus der Datenbank beim ersten Zugriff darauf wie folgt implementieren:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-keyword\">private<\/span> Settings settings;\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">private<\/span> Settings <span class=\"hljs-title\">getSettings<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n  <span class=\"hljs-keyword\">if<\/span> (settings == <span class=\"hljs-keyword\">null<\/span>) {\n    settings = loadSettingsFromDatabase();\n  }\n  <span class=\"hljs-keyword\">return<\/span> settings;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Beim ersten Aufruf der Methode werden die Settings geladen und im <code>settings<\/code>-Feld gespeichert. Bei jedem weiteren Aufruf wird das im <code>settings<\/code>-Feld gespeicherte Objekt zur\u00fcckgegeben.<\/p>\n\n\n\n<p>Diese Methode ist jedoch nicht threadsicher.<\/p>\n\n\n\n<p>Es gibt drei problematische Effekte, die beim Aufruf dieser Methode aus mehreren gleichzeitig laufenden Threads auftreten kann \u2013 einen offensichtlichen und zwei, die selbst erfahrene Java-EntwicklerInnen oft \u00fcbersehen.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"effekt-1-mehrfache-initialisierung-durch-verzahnung-der-thread-ausfuehrungen\">Effekt 1: Mehrfache Initialisierung durch Verzahnung der Thread-Ausf\u00fchrungen<\/h3>\n\n\n\n<p>Ein Effekt, den die meisten Java Developer sofort sehen, ist der folgende:<\/p>\n\n\n\n<p>W\u00fcrde die Methode aus zwei Threads gleichzeitig aufgerufen werden, kann sich die Ausf\u00fchrung der zwei Threads wie folgt verzahnen:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full_800\"><img decoding=\"async\" width=\"800\" height=\"308\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-race-condition-1.v2-800x308.png\" alt=\"Mehrfache Initialisierung eines Feldes durch Verzahnung der Thread-Ausf\u00fchrungen\" class=\"wp-image-48961\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-race-condition-1.v2-800x308.png 800w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-race-condition-1.v2-224x86.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-race-condition-1.v2-336x129.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-race-condition-1.v2-504x194.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-race-condition-1.v2-672x259.png 672w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-race-condition-1.v2-400x154.png 400w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-race-condition-1.v2-600x231.png 600w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-race-condition-1.v2-944x363.png 944w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-race-condition-1.v2-1200x462.png 1200w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-race-condition-1.v2.png 1600w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><\/figure>\n<\/div>\n\n\n<p>Vereinfacht gesagt:<\/p>\n\n\n\n<p>Wenn beide Threads die Methode nahezu gleichzeitig starten, sehen beide, dass <code>settings null<\/code> ist. Dementsprechend w\u00fcrden beide Threads die Settings aus der Datenbank laden, und die als zweites geladenen Settings w\u00fcrden die zuerst geladenen \u00fcberschreiben.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"effekt-2-mehrfache-initialisierung-durch-cache-effekte\">Effekt 2: Mehrfache Initialisierung durch Cache-Effekte<\/h3>\n\n\n\n<p>Was selbst erfahrene EntwicklerInnen oft <em>nicht<\/em> sehen, ist folgendes:<\/p>\n\n\n\n<p>Tats\u00e4chlich muss die Thread-Ausf\u00fchrung nicht einmal verzahnt sein. Auch wenn ein Thread die Methode erst dann ausf\u00fchrt, nachdem der andere sie beendet hat, kann es zu mehrfacher Initialisierung kommen:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full_800\"><img decoding=\"async\" width=\"800\" height=\"308\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-race-condition-2.v2-800x308.png\" alt=\"Mehrfache Initialisierung eines Feldes durch Cache-Effekte\" class=\"wp-image-48960\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-race-condition-2.v2-800x308.png 800w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-race-condition-2.v2-224x86.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-race-condition-2.v2-336x129.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-race-condition-2.v2-504x194.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-race-condition-2.v2-672x259.png 672w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-race-condition-2.v2-400x154.png 400w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-race-condition-2.v2-600x231.png 600w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-race-condition-2.v2-944x363.png 944w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-race-condition-2.v2-1200x462.png 1200w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-race-condition-2.v2.png 1600w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><\/figure>\n<\/div>\n\n\n<p>Wie kann das sein? Wieso sollte Thread 2 <code>settings<\/code> als <code>null<\/code> sehen und die Initialisierung wiederholen?<\/p>\n\n\n\n<p>Die Antwort liegt in der CPU-Architektur:<\/p>\n\n\n\n<p>Jeder CPU-Kern hat einen Cache, in dem Daten aus dem Hauptspeicher zwischengespeichert werden. Genauer gesagt: Bei modernen CPUs hat jeder Kern sogar zwei Caches: einen Level-1-Cache und einen Level-2-Cache. Zudem hat die CPU noch einen von allen Kernen geteilten Level-3-Cache:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full_800\"><img decoding=\"async\" width=\"800\" height=\"371\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-cpu-caches.v2-800x371.png\" alt=\"CPU-Kern-Caches L1, L2 und gemeinsam genutzter CPU-Cache L3\" class=\"wp-image-48963\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-cpu-caches.v2-800x371.png 800w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-cpu-caches.v2-224x104.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-cpu-caches.v2-336x156.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-cpu-caches.v2-504x234.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-cpu-caches.v2-672x311.png 672w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-cpu-caches.v2-400x185.png 400w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-cpu-caches.v2-600x278.png 600w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-cpu-caches.v2-944x438.png 944w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-cpu-caches.v2-1200x556.png 1200w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-cpu-caches.v2.png 1601w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><\/figure>\n<\/div>\n\n\n<p>Die Aufteilung auf drei Cache-Level k\u00f6nnen wir f\u00fcr die Betrachtung der Auswirkungen der Caches allerdings vernachl\u00e4ssigen, und daher werde ich im folgenden nur vom CPU-Kern-Cache sprechen.<\/p>\n\n\n\n<p>Aus Performancegr\u00fcnden arbeitet jeder CPU-Kern vorrangig mit seinem Cache.<\/p>\n\n\n\n<p>Nehmen wir an, Thread 1 und Thread 2 laufen auf unterschiedlichen CPU-Kernen \u2013 der Einfachheit halber: CPU-Kern 1 und CPU-Kern 2. Dann k\u00f6nnte folgendes passieren:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Thread 1 hat das <code>settings<\/code>-Feld bisher nur in den Cache von CPU-Kern 1 geschrieben, aber noch nicht in den Hauptspeicher. Thread 2 l\u00e4dt das <code>settings<\/code>-Feld aus dem Hauptspeicher, wo es noch <code>null<\/code> ist. Demzufolge sieht Thread 2 es als <code>null<\/code> und initialisiert es erneut.<\/li>\n\n\n\n<li>Oder: Thread 1 schreibt das <code>settings<\/code>-Feld zwar in den Hauptspeicher, aber CPU-Kern 2 hat das Feld bereits zuvor, als es noch <code>null<\/code> war, in seinen Cache geladen. Thread 2 greift nun auf dieses gecachte Feld zu \u2013 sieht also auch in diesem Fall <code>null<\/code> und initialisiert die Settings erneut.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"race-conditions\">Race Conditions<\/h3>\n\n\n\n<p>Beide Effekte \u2013 sowohl das wiederholte Laden durch Verzahnung der Thread-Ausf\u00fchrungen als auch das wiederholte Laden durch Cache-Effekte \u2013 treten nicht deterministisch auf, da nicht vorhersehbar ist, wie Threads zeitlich ablaufen und wann die CPU den Cache mit dem Hauptspeicher synchronisiert. Es handelt sich daher um sogenannte \u201eRace Conditions\u201c.<\/p>\n\n\n\n<p>D. h. dass die Software unter Umst\u00e4nden monatelang korrekt l\u00e4uft, bis es zu einem Fehler kommt. Es ist dann extrem schwer, den Fehler zu reproduzieren, ihn aufzusp\u00fcren und zu beheben.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"und-wenn-uns-die-wiederholte-initialisierung-nicht-stoert\">Und wenn uns die wiederholte Initialisierung nicht st\u00f6rt?<\/h3>\n\n\n\n<p>Wenn diese Race Condition nur alle paar Monate auftritt und die einzige Folge ist, dass die Einstellungen wiederholt aus der Datenbank geladen werden \u2013 k\u00f6nnten wir das nicht einfach ignorieren?<\/p>\n\n\n\n<p>Zum einen: In diesem konkreten Use Case vermutlich schon. Es gibt aber auch Use Cases, in denen eine wiederholte Initialisierung ernste Konsequenzen haben k\u00f6nnte. Beispielsweise, wenn das so initialisierte Objekt einen globalen Status kapselt, auf den auch schreibend zugegriffen wird. In so einem Use Case m\u00fcssen wir sicherstellen, dass zu jeder Zeit nur <em>eine<\/em> Instanz existiert.<\/p>\n\n\n\n<p>Zum anderen: Ich habe oben erw\u00e4hnt, dass es neben dem offensichtlichen Effekt der ungl\u00fccklichen Thread-Verzahnung <em>zwei<\/em> subtile Effekte gibt. Der eine war das CPU Core Caching. <\/p>\n\n\n\n<p>Bevor ich auf den zweiten subtilen Effekt n\u00e4her eingehe, zeige ich dir zun\u00e4chst die naheliegendste (aber auch unperformanteste) L\u00f6sung, um diese Methode threadsicher zu machen.<\/p>\n\n\n\n<p>Danach zeige ich dir das \u201eoriginale\u201c Double-Checked Locking, das diese unperformante L\u00f6sung performanter machen soll. Leider enth\u00e4lt das \u201eoriginale\u201c Double-Checked Locking immer noch den zweiten subtilen Effekt.<\/p>\n\n\n\n<p>Dann werde ich diesen Effekt erkl\u00e4ren und dir zeigen, wie du Double-Checked Locking in Java korrekt und performant implementierst.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"unperformanteste-loesung-vollstaendige-synchronisation\">Unperformanteste L\u00f6sung: Vollst\u00e4ndige Synchronisation<\/h2>\n\n\n\n<p>Die naheliegendste Variante, um die <code>getSettings()<\/code>-Methode threadsicher zu machen, ist sie mit dem <code>synchronized<\/code>-Keyword (oder alternativ einem expliziten Lock) zu synchronisieren:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-comment\">\/\/           \u2193<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">synchronized<\/span> Settings <span class=\"hljs-title\">getSettings<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n  <span class=\"hljs-keyword\">if<\/span> (settings == <span class=\"hljs-keyword\">null<\/span>) {\n    settings = loadSettingsFromDatabase();\n  }\n  <span class=\"hljs-keyword\">return<\/span> settings;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Das bedeutet allerdings, dass bei <em>jedem<\/em> Zugriff auf die Einstellungen eine vollst\u00e4ndige Synchronisation durchgef\u00fchrt wird, d. h. dass ggf. gewartet werden muss, falls gerade ein anderer Thread die Methode blockiert, dass dann der Zugriff f\u00fcr andere Threads blockiert wird und dass beim Betreten und Verlassen des <code>synchronized<\/code>-Blocks alle Daten zwischen CPU-Cache und Hauptspeicher synchronisisert werden.<\/p>\n\n\n\n<p>Dieser Vorgang ist recht aufw\u00e4ndig und daher inbesondere f\u00fcr h\u00e4ufig aufgerufene Methoden nicht zu empfehlen.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"das-originale-double-checked-locking\">Das \u201eoriginale\u201c Double-Checked Locking<\/h2>\n\n\n\n<p>Oft versuchen Entwickler, den Code zu optimieren, indem sie zun\u00e4chst pr\u00fcfen, ob das <code>settings<\/code>-Feld bereits gesetzt ist, und nur dann den synchronisierten Block betreten, wenn das Feld noch <code>null<\/code> ist und dementsprechend initialisiert werden muss.<\/p>\n\n\n\n<p>F\u00fcr den Fall, dass <em>nach<\/em> der ersten Pr\u00fcfung \u2013 aber <em>vor<\/em> dem Betreten des <code>synchronized<\/code>-Blocks \u2013 ein anderer Thread das <code>settings<\/code>-Feld gesetzt hat, wird innerhalb des <code>synchronized<\/code>-Blocks ein zweites Mal gepr\u00fcft, ob dieses <code>null<\/code> ist. Daher die Bezeichnung \u201eDouble-Checked Locking\u201c (auf deutsch: \u201eDoppelt \u00fcberpr\u00fcfte Sperrung\u201c):<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-comment\">\/\/ Don't do this!!!<\/span>\n<span class=\"hljs-comment\">\/\/ This is the original, broken implementation of the \"Double-Checked Locking\" idiom.<\/span>\n<span class=\"hljs-keyword\">private<\/span> Settings settings;\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">private<\/span> Settings <span class=\"hljs-title\">getSettings<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n  <span class=\"hljs-keyword\">if<\/span> (settings == <span class=\"hljs-keyword\">null<\/span>) {\n    <span class=\"hljs-keyword\">synchronized<\/span> (<span class=\"hljs-keyword\">this<\/span>) {\n      <span class=\"hljs-keyword\">if<\/span> (settings == <span class=\"hljs-keyword\">null<\/span>) {\n        settings = loadSettingsFromDatabase();\n      }\n    }\n  }\n  <span class=\"hljs-keyword\">return<\/span> settings;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Auf den ersten Blick scheint dies eine sinnvolle L\u00f6sung zu sein, da so \u2013 au\u00dfer beim ersten Aufruf \u2013 keine teure Synchronisation stattfinden muss. Doch wie der Kommentar in den ersten Zeilen des Codes bereits offenbart, ist diese Implementierung fehlerhaft.<\/p>\n\n\n\n<p>Und damit kommen wir zum dritten Effekt.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"effekt-3-instruction-reordering\">Effekt 3: Instruction Reordering<\/h3>\n\n\n\n<p>\u201eInstruction Reordering\u201c bedeutet, dass sowohl Compiler als auch CPU innerhalb eines Threads zur Performance-Optimierung CPU-Instruktionen umsortieren d\u00fcrfen, d. h. in einer anderen Reihenfolge ausf\u00fchren d\u00fcrfen \u2013 solange das die Semantik der Programmausf\u00fchrung <em>innerhalb dieses Threads<\/em> nicht ver\u00e4ndert.<\/p>\n\n\n\n<p>Beispielsweise k\u00f6nnte der Code <code>int a = 3; int b = 4; int c = a + b;<\/code> auch so compiliert oder ausgef\u00fchrt werden, dass zuerst Variable <code>b<\/code> auf 4 gesetzt wird und danach Variable <code>a<\/code> auf 3. F\u00fcr die Berechnung von <code>c<\/code> macht das keinen Unterschied:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-half_600\"><img decoding=\"async\" width=\"600\" height=\"196\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/instruction-reordering-example-600x196.png\" alt=\"Instruction-Reordering-Beispiel\" class=\"wp-image-48965\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/instruction-reordering-example-600x196.png 600w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/instruction-reordering-example-224x73.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/instruction-reordering-example-336x110.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/instruction-reordering-example-504x165.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/instruction-reordering-example-672x220.png 672w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/instruction-reordering-example-400x131.png 400w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/instruction-reordering-example-800x261.png 800w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/instruction-reordering-example-944x308.png 944w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/instruction-reordering-example.png 1200w\" sizes=\"(max-width: 600px) 100vw, 600px\" \/><\/figure>\n<\/div>\n\n\n<p>Was hat das mit der Initialisierung des <code>settings<\/code>-Felds zu tun?<\/p>\n\n\n\n<p>Der oben gezeigte Java-Programmcode wird sinngem\u00e4\u00df in die folgenden Instruktionen \u00fcbersetzt:<\/p>\n\n\n\n<ol class=\"wp-block-list hc-list-without-gaps\">\n<li>Lade die Einstellungen aus der Datenbank.<\/li>\n\n\n\n<li>Erzeuge ein neues <code>Settings<\/code>-Objekt.<\/li>\n\n\n\n<li>Initialisiere das <code>Settings<\/code>-Objekt mit den aus der Datenbank geladenen Werten.<\/li>\n\n\n\n<li>Weise dieses <code>Settings<\/code>-Objekt dem <code>settings<\/code>-Feld zu.<\/li>\n\n\n\n<li>Lese Einstellungen aus dem <code>settings<\/code>-Feld<br>(dieser Schritt ist im Beispielcode oben nicht zu sehen).<\/li>\n<\/ol>\n\n\n\n<p>Der Java-Compiler darf diese Instruktionen umordnen, insbesondere darf er die Schritte 3 und 4 vertauschen:<\/p>\n\n\n\n<ol class=\"wp-block-list hc-list-without-gaps\">\n<li>Lade die Einstellungen aus der Datenbank.<\/li>\n\n\n\n<li>Erzeuge ein neues <code>Settings<\/code>-Objekt.<\/li>\n\n\n\n<li><strong>Weise dieses <code>Settings<\/code>-Objekt dem <code>settings<\/code>-Feld zu.<\/strong><\/li>\n\n\n\n<li><strong>Initialisiere das <code>Settings<\/code>-Objekt mit den aus der Datenbank geladenen Werten.<\/strong><\/li>\n\n\n\n<li>Lese Einstellungen aus dem <code>settings<\/code>-Feld.<\/li>\n<\/ol>\n\n\n\n<p>Das <code>Settings<\/code>-Objekt wird also zuerst (im uninitialisierten Zustand) dem Feld zugewiesen und erst danach initialisiert. Innerhalb eines Threads spielt das keine Rolle, da Schritt 5 (der lesende Zugriff) immer noch <em>nach<\/em> der Initialisierung ausgef\u00fchrt wird.<\/p>\n\n\n\n<p>Aber: Wenn wir das noch einmal im Kontext der verzahten Thread-Ausf\u00fchrung betrachten, w\u00e4re jetzt folgender Ablauf m\u00f6glich:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full_800\"><img decoding=\"async\" width=\"800\" height=\"514\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-race-condition-3.v2-800x514.png\" alt=\"Effekt von Instruction Reordering auf Double-Checked Locking\" class=\"wp-image-48958\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-race-condition-3.v2-800x514.png 800w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-race-condition-3.v2-224x144.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-race-condition-3.v2-336x216.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-race-condition-3.v2-504x324.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-race-condition-3.v2-672x432.png 672w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-race-condition-3.v2-400x257.png 400w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-race-condition-3.v2-600x386.png 600w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-race-condition-3.v2-944x607.png 944w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-race-condition-3.v2-1200x771.png 1200w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-race-condition-3.v2.png 1600w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><\/figure>\n<\/div>\n\n\n<p>Was passiert hier?<\/p>\n\n\n\n<p>Thread 1 sieht das uninitialisierte <code>settings<\/code>-Feld, l\u00e4dt die Einstellungen aus der Datenbank, erzeugt ein <code>Settings<\/code>-Objekt und weist dieses dem <code>settings<\/code>-Feld zu \u2013 und zwar bevor es initialisiert ist. CPU-Kern 1 speichert zuf\u00e4llig in diesem Moment das <code>settings<\/code>-Feld im Hauptspeicher.<\/p>\n\n\n\n<p>Genau jetzt l\u00e4dt CPU-Kern 2 das <code>settings<\/code>-Feld aus dem Hauptspeicher. Und da dieses nicht <code>null<\/code> ist, versucht Thread 2 nicht, den <code>synchronized<\/code>-Block zu betreten, merkt dementsprechend auch nicht, dass Thread 1 die Initialisierung noch nicht abgeschlossen hat.<\/p>\n\n\n\n<p>Und somit sieht Thread 2 zu diesem Zeitpunkt <em>uninitialisierte<\/em> Einstellungen (d. h. beispielsweise <code>int<\/code>-Felder, die noch auf 0 stehen, oder String-Felder, die noch <code>null<\/code> sind).<\/p>\n\n\n\n<p>Das passiert nat\u00fcrlich nicht <em>immer<\/em>, sondern nur dann, wenn die Ausf\u00fchrung der zwei Threads und die Synchronisation zwischen CPU-Kern-Cache und Hauptspeicher exakt so verzahnt abl\u00e4uft, wie oben dargestellt. Auch mit fehlerhaftem Double-Checked Locking kann die Anwendung monatelang korrekt laufen, bis es zu einem Fehler kommt. Doch dann wird es quasi unm\u00f6glich sein, den Fehler zu reproduzieren \u2013 und dementsprechend schwer, ihn aufzusp\u00fcren und zu beheben.<\/p>\n\n\n\n<p>Auch bei der <em>nicht<\/em> synchronisierten Version vom Beginn des Artikels kann diese Race Condition auftreten. D. h. die Frage von oben (\u201eUnd wenn uns die wiederholte Initialisierung nicht st\u00f6rt?\u201c) m\u00fcssen wir damit beantworten, dass wir selbst dann den Zugriff auf das gemeinsam genutzte Feld geeignet synchronisieren m\u00fcssen.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"warum-ueberhaupt-cpu-kern-cache-und-instruction-reordering\">Warum \u00fcberhaupt CPU-Kern-Cache und Instruction Reordering?<\/h3>\n\n\n\n<p>Jetzt stellt sich berechtigterweise die Frage:<\/p>\n\n\n\n<p>Warum wird der CPU-Kern-Cache \u00fcberhaupt verwendet, und warum erlaubt Java \u201eInstruction Reordering\u201c, wenn das doch zu so vielen Problemen f\u00fchren kann?<\/p>\n\n\n\n<p>Die Antwort liegt in der Grundannahme, auf der CPU-Architekturen und Compiler basieren:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-style-default is-layout-flow wp-block-quote-is-layout-flow\">\n<p>Anwendungen sollen so performant wie m\u00f6glich ablaufen, und bei Multithreading-Anwendungen wird davon ausgegangen, dass per default Threads unabh\u00e4ngig voneinander sind und deren Performance jeweils einzeln optimiert werden soll.<\/p>\n\n\n\n<p><strong>Zugriff auf gemeinsam genutzte Datenstrukturen ist die Ausnahme und muss vom Programmierer durch geeignete Ma\u00dfnahmen synchronisiert werden.<\/strong><\/p>\n<\/blockquote>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><\/p>\n<\/blockquote>\n\n\n\n<p>Das \u201eoriginale\u201c Double-Checked Locking ist \u2013 wie wir nun gesehen haben \u2013 <em>keine<\/em> geeignete Ma\u00dfnahme. Wie macht man es nun richtig?<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"korrektes-double-checked-locking-in-java\">Korrektes Double-Checked Locking in Java<\/h2>\n\n\n\n<p>Vor Java 5 gab es keine M\u00f6glichkeit, das Double-Checked Locking korrekt in Java umzusetzen. Ab Java 5 ist dies mit einem einzigen zus\u00e4tzlichen Keyword m\u00f6glich: <code>volatile<\/code>.<\/p>\n\n\n\n<p>Hier ist eine korrekte (noch nicht optimierte) Version des Double-Checked Locking Idioms in Java:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-comment\">\/\/ Correct - but not yet optimized - version of the \"Double-Checked Locking\" idiom<\/span>\n<span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">volatile<\/span> Settings settings; <span class=\"hljs-comment\">\/\/ \u27f5 `settings` field must be volatile!<\/span>\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">private<\/span> Settings <span class=\"hljs-title\">getSettings<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n  <span class=\"hljs-keyword\">if<\/span> (settings == <span class=\"hljs-keyword\">null<\/span>) {\n    <span class=\"hljs-keyword\">synchronized<\/span> (<span class=\"hljs-keyword\">this<\/span>) {\n      <span class=\"hljs-keyword\">if<\/span> (settings == <span class=\"hljs-keyword\">null<\/span>) {\n        settings = loadSettingsFromDatabase();\n      }\n    }\n  }\n  <span class=\"hljs-keyword\">return<\/span> settings;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Was macht <code>volatile<\/code>, und warum ist das Double-Checked Locking damit korrekt?<\/p>\n\n\n\n<p>Mit dem Keyword <code>volatile<\/code> zeigen wir an, dass der Wert eines Feldes von verschiedenen Threads ge\u00e4ndert werden kann. Damit erreichen wir zwei Dinge:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Zwischen Schreiben und nachfolgendem Lesen eines Feldes werden die Caches der beteiligten CPU-Kerne mit dem Hauptspeicher synchronisiert. Somit sind \u00c4nderungen an einem Feld immer f\u00fcr andere Threads sichtbar \u2013 Thread-Caching-Probleme werden so verhindert.<\/li>\n\n\n\n<li>Ein neu erzeugtes Objekt wird erst dann einem Feld zugewiesen, wenn es vollst\u00e4ndig initialisiert ist \u2013 damit kann ein Thread nie ein unvollst\u00e4ndig initialisiertes Objekt aus einem anderen Thread sehen.<\/li>\n<\/ol>\n\n\n\n<p>Was bedeutet das konkret f\u00fcr das Double-Checked Locking?<\/p>\n\n\n\n<p>Durch <code>volatile<\/code> wird sichergestellt, dass im vorherigen Beispiel kein Instruction Reordering bzgl. der Objekt-Initialisierung durchgef\u00fchrt wird, d. h. die Schritte 3 und 4 d\u00fcrfen nicht vertauscht werden. Somit ist die oben dargestellte, zu einer Race Condition f\u00fchrende Thread-Verzahnung nicht mehr m\u00f6glich.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"optimiertes-double-checked-locking-in-java\">Optimiertes Double-Checked Locking in Java<\/h2>\n\n\n\n<p><code>volatile<\/code> f\u00fchrt allerdings auch dazu, dass, nachdem <code>settings<\/code> initialisiert wurde, bei <em>jedem<\/em> Aufruf der <code>getSettings()<\/code>-Methode <em>zwei<\/em> Mal der CPU-Cache mit dem Hauptspeicher synchronisiert wird \u2013 denn es wird zwei Mal auf das <code>settings<\/code>-Feld zugegriffen: einmal bei der Pr\u00fcfung auf <code>null<\/code> und einmal bei der R\u00fcckgabe mit <code>return<\/code>.<\/p>\n\n\n\n<p>Das k\u00f6nnen wir optimieren, indem wir das Feld <code>settings<\/code> zun\u00e4chst einer lokalen Variable zuweisen. Diese kann, da jeder Thread seine eigene lokale Version diese Variablen hat, auf dem Thread-Stack gehalten werden und muss nicht mit dem Hauptspeicher synchronisiert werden:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-comment\">\/\/ Correct and optimized version of the \"Double-Checked Locking\" idiom<\/span>\n<span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">volatile<\/span> Settings settings;\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">private<\/span> Settings <span class=\"hljs-title\">getSettings<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n  Settings localRef = settings; <span class=\"hljs-comment\">\/\/ \u27f5 Store `settings` in a thread-local variable<\/span>\n  <span class=\"hljs-keyword\">if<\/span> (localRef == <span class=\"hljs-keyword\">null<\/span>) {\n    <span class=\"hljs-keyword\">synchronized<\/span> (<span class=\"hljs-keyword\">this<\/span>) {\n      localRef = settings;\n      <span class=\"hljs-keyword\">if<\/span> (localRef == <span class=\"hljs-keyword\">null<\/span>) {\n        settings = localRef = loadSettingsFromDatabase();\n      }\n    }\n  }\n  <span class=\"hljs-keyword\">return<\/span> localRef; <span class=\"hljs-comment\">\/\/ \u27f5 Return thread-local variable<\/span>\n                   <span class=\"hljs-comment\">\/\/   without accessing main memory a second time<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Im regul\u00e4ren Fall, also wenn <code>settings<\/code> bereits initialisiert ist, wird so nur noch einmal auf den Hauptspeicher zugegriffen: beim Zuweisen von <code>localRef<\/code> auf <code>settings<\/code>. Die R\u00fcckgabe mit <code>return<\/code> greift dann nur noch auf die Thread-lokale Variable <code>localRef<\/code> zu.<\/p>\n\n\n\n<p>Klingt kompliziert?<\/p>\n\n\n\n<p>Ist es auch! Und damit besteht auch immer das Risiko einer fehlerhaften Implementierung.<\/p>\n\n\n\n<p>F\u00fcr die Initialisierung von zumindest statischen Feldern gibt es eine weitere Variante: das <a href=\"https:\/\/www.happycoders.eu\/de\/java\/initialization-on-demand-holder-idiom\/\">Initialization-on-Demand Holder Idiom<\/a> \u2013 aber auch das ist eher ein Workaround als eine L\u00f6sung.<\/p>\n\n\n\n<p>Geht das nicht sch\u00f6ner?<\/p>\n\n\n\n<p>Bald! In <a href=\"https:\/\/www.happycoders.eu\/de\/java\/java-25-features\/#stable-values-preview-jep-502\">Java 25<\/a> wurden die sogenannten <em>Stable Values<\/em> als Preview-Version eingef\u00fchrt. In <a href=\"https:\/\/www.happycoders.eu\/de\/java\/java-26-features\/#lazy-constants-second-preview-jep-526\">Java 26<\/a> wurden sie in <a href=\"https:\/\/www.happycoders.eu\/de\/java\/lazy-constants\/\">Lazy Constants<\/a> umbenannt. <em>Lazy Constants<\/em> sind ein Wrapper, der die threadsichere Initialisierung von Konstanten beim ersten Zugriff darauf hinter einer einfachen API kapselt.<\/p>\n\n\n\n<p>Bis <em>Lazy Constants<\/em> finalisiert werden, m\u00fcssen wir uns allerdings zwischen einer vollst\u00e4ndigen Synchronisation (einfach in der Implementierung, daf\u00fcr langsam) und einem korrekt implementierten Double-Checked Locking (schnell, daf\u00fcr kompliziert und fehleranf\u00e4llig in der Implementierung) entscheiden.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"fazit\">Fazit<\/h2>\n\n\n\n<p>Die Initialisierung von gemeinsam genutzten Objekten beim ersten Zugriff darauf kann in Multithreading-Anwendungen (bisher) nur durch vollst\u00e4ndige Synchronisation (mit <code>synchronized<\/code> oder einem expliziten Lock), durch ein korrekt implementiertes Double-Checked Locking oder durch das <a href=\"https:\/\/www.happycoders.eu\/de\/java\/initialization-on-demand-holder-idiom\/\">Initialization-on-Demand Holder Idiom<\/a> implementiert werden.<\/p>\n\n\n\n<p>Beim Double-Checked Locking ist es essentiell, das gemeinsam genutzte Feld als <code>volatile<\/code> zu markieren, um Race Conditions auszuschlie\u00dfen, die durch Thread-Caching-Effekte oder Instruction Reordering verursacht werden.<\/p>\n\n\n\n<p>In Java 26 stehen <a href=\"https:\/\/www.happycoders.eu\/de\/java\/lazy-constants\/\">Lazy Constants<\/a> als Preview-Feature zur Verf\u00fcgung: ein threadsicherer und Performance-optimierter Wrapper f\u00fcr Objekte, die beim ersten Zugriff initialisiert werden sollen.<\/p>\n<aside><p>Wenn dir der Artikel weitergeholfen hat, w\u00fcrde ich mich sehr \u00fcber eine positive Bewertung auf meinem <a href=\"https:\/\/www.provenexpert.com\/de-de\/sven-woltmann-happycoders-eu\/7smk\/\" target=\"_blank\" rel=\"noopener\">ProvenExpert-Profil<\/a> freuen. Dein Feedback hilft mir, meine Inhalte weiter zu verbessern und motiviert mich, neue informative Artikel zu schreiben.<\/p>\r\n                        <p>\ud83d\udc49 <a href=\"https:\/\/www.provenexpert.com\/de-de\/sven-woltmann-happycoders-eu\/7smk\/\" target=\"_blank\" rel=\"noopener\">Bewertung abgeben<\/a><\/p>\r\n                        <p>Du m\u00f6chtest \u00fcber alle neue Java-Features auf dem Laufenden sein? Dann <a href=\"#\" data-formkit-toggle=\"d8ee997126\">klicke hier<\/a>, um dich f\u00fcr den HappyCoders-Newsletter anzumelden.<\/p>\r\n                        <p>\ud83d\udc49 <a href=\"#\" data-formkit-toggle=\"d8ee997126\">Newsletter-Anmeldung<\/a><\/p><\/aside>","protected":false},"excerpt":{"rendered":"<p>Entdecke das Double-Checked Locking Pattern in Java: Optimiere Thread-sichere, lazy Objektinitialisierung ohne Performance-Einbu\u00dfen.<\/p>\n","protected":false},"author":1,"featured_media":48854,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_seopress_robots_primary_cat":"","_seopress_titles_title":"","_seopress_titles_desc":"Entdecke das Double-Checked Locking Pattern in Java: Optimiere Thread-sichere, lazy Objektinitialisierung ohne Performance-Einbu\u00dfen.","_seopress_robots_index":"","_uag_custom_page_level_css":"","_wp_convertkit_post_meta":{"form":"-1","landing_page":"","tag":"0","restrict_content":"0"},"_metis_text_type":"standard","_metis_text_length":16834,"_post_count":0,"footnotes":""},"categories":[64],"tags":[165],"class_list":["post-48849","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-java","tag-java-fortgeschritten"],"uagb_featured_image_src":{"full":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-hero-image.jpg",1770,986,false],"thumbnail":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-hero-image.jpg",150,84,false],"medium":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-hero-image.jpg",300,167,false],"medium_large":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-hero-image.jpg",768,428,false],"large":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-hero-image.jpg",1024,570,false],"feature_thumb_224":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-hero-image-224x125.jpg",224,125,true],"feature_thumb_336":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-hero-image-336x187.jpg",336,187,true],"feature_thumb_504":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-hero-image-504x281.jpg",504,281,true],"feature_thumb_672":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-hero-image-672x374.jpg",672,374,true],"half_400":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-hero-image-400x223.jpg",400,223,true],"half_600":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-hero-image-600x334.jpg",600,334,true],"full_800":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-hero-image-800x446.jpg",800,446,true],"full_944":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-hero-image-944x526.jpg",944,526,true],"full_1200":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-hero-image-1200x668.jpg",1200,668,true],"wide_1180":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-hero-image-1180x490.jpg",1180,490,true],"wide_1770":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-hero-image-1770x735.jpg",1770,735,true],"1536x1536":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-hero-image.jpg",1536,856,false],"2048x2048":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2025\/05\/double-checked-locking-hero-image.jpg",1770,986,false]},"uagb_author_info":{"display_name":"Sven Woltmann","author_link":"https:\/\/www.happycoders.eu\/de\/author\/sven\/"},"uagb_comment_info":0,"uagb_excerpt":"Entdecke das Double-Checked Locking Pattern in Java: Optimiere Thread-sichere, lazy Objektinitialisierung ohne Performance-Einbu\u00dfen.","public_identification_id":"120fdbc676624004a81a71ea18a4f606","private_identification_id":"507398805cca4efab7be3b25d23f5561","_links":{"self":[{"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/posts\/48849","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/comments?post=48849"}],"version-history":[{"count":10,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/posts\/48849\/revisions"}],"predecessor-version":[{"id":54115,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/posts\/48849\/revisions\/54115"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/media\/48854"}],"wp:attachment":[{"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/media?parent=48849"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/categories?post=48849"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/tags?post=48849"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}