Heim  >  Artikel  >  Java  >  Haben Sie das richtige Schloss verwendet? Eine kurze Diskussion über Java-„Sperren“-Angelegenheiten

Haben Sie das richtige Schloss verwendet? Eine kurze Diskussion über Java-„Sperren“-Angelegenheiten

Java学习指南
Java学习指南nach vorne
2023-07-26 16:27:15898Durchsuche

In jeder Zeit werden diejenigen, die wissen, wie man lernt, nicht schlecht behandelt.

Ich habe kürzlich festgestellt, dass neue Kollegen im Unternehmen einige Missverständnisse über Schlösser haben. Deshalb sprechen wir heute über „Schlösser“ und gleichzeitige Sicherheitscontainer Java Welche Vorsichtsmaßnahmen gelten für die Verwendung?

Aber vorher müssen wir noch erklären, warum wir dieses Ding sperren müssen. Dies beginnt mit der Quelle des Parallelitätsfehlers.

Die Quelle von Parallelitätsfehlern

Ich habe 2019 einen Artikel zu diesem Thema geschrieben. Wenn ich jetzt auf diesen Artikel zurückblicke, ist es mir wirklich peinlich.

Haben Sie das richtige Schloss verwendet? Eine kurze Diskussion über Java-„Sperren“-Angelegenheiten

Lassen Sie uns einen Blick auf die Quelle werfen. Wir wissen, dass die Lesegeschwindigkeit der Festplatte am langsamsten ist, gefolgt vom Lesen des Speichers . Das Lesen des Speichers ist langsamer als der Betrieb der CPU. Es war zu langsam, also habe ich einen weiteren CPU-Cache erstellt, L1, L2 und L3.

Es ist dieser CPU-Cache in Verbindung mit der aktuellen Multi-Core-CPU-Situation, der einen Parallelitätsfehler erzeugt.

Haben Sie das richtige Schloss verwendet? Eine kurze Diskussion über Java-„Sperren“-Angelegenheiten

Dies ist ein sehr einfacher Code. Wenn Thread A und Thread B diese Methode in CPU-A bzw. CPU-B ausführen, besteht ihre Operation darin, zuerst vom Hauptspeicher auf den CPU-Eingang zuzugreifen Im Cache ist der Wert von a in ihrem Cache zu diesem Zeitpunkt alle 0.

Dann führen sie jeweils a++ aus. Zu diesem Zeitpunkt ist der Wert von a in ihren Augen 1. Wenn a später in den Hauptspeicher geflasht wird, ist der Wert von a immer noch 1. Das ist offensichtlich Die letzte Addition von Eins wird zweimal ausgeführt. Das Ergebnis ist 1, nicht 2.

Dieses Problem wird Sichtbarkeitsproblem genannt.

Wenn man sich unsere a++-Anweisung ansieht, sind sie alle Hochsprachen. Tatsächlich scheint es sehr praktisch zu sein, sie zu verwenden Anweisungen, die wirklich ausgeführt werden müssen.

Eine Anweisung in einer Hochsprache kann in mehr als eine CPU-Anweisung übersetzt werden. Beispielsweise kann a++ in mindestens drei CPU-Anweisungen übersetzt werden.

  • GET A Vom Memory to Registration; Unterbrechen Sie eine Anweisung, da sie atomar ist. Tatsächlich kann die CPU eine Anweisung ausführen, wenn die Zeitscheibe abgelaufen ist. Zu diesem Zeitpunkt wechselt der Kontext zu einem anderen Thread, der ebenfalls a++ ausführt. Wenn Sie wieder zurückschalten, ist der Wert von a tatsächlich falsch.

  • Dieses Problem wird Atomizitätsproblem genannt
  • .

    Und um die Leistung zu optimieren, kann der Compiler oder Interpreter die Ausführungsreihenfolge von Anweisungen ändern. Das klassischste Beispiel ist die doppelte Überprüfung des Singleton-Modus. Um die Ausführungseffizienz zu verbessern, führt die CPU beispielsweise eine andere Reihenfolge aus. Wenn die CPU beispielsweise auf das Laden von Speicherdaten wartet, stellt sie fest, dass die folgende Additionsanweisung nicht vom Berechnungsergebnis der vorherigen Anweisung abhängt Es führt zuerst die Additionsanweisung aus.

  • Dieses Problem wird als Bestellproblem bezeichnet
  • .

    Jetzt haben wir die Ursachen von Parallelitätsfehlern analysiert, nämlich diese drei Hauptprobleme. Es ist ersichtlich, dass es tatsächlich notwendig ist, ob es sich um CPU-Cache, Multi-Core-CPU, Hochsprache oder Umordnung außerhalb der Reihenfolge handelt, sodass wir diesen Problemen nur direkt begegnen können.

    Um diese Probleme zu lösen, müssen Sie das Caching deaktivieren, die Neuanordnung von Compileranweisungen, den gegenseitigen Ausschluss usw. verbieten. Heute befasst sich unser Thema mit dem gegenseitigen Ausschluss.
Gegenseitiger Ausschluss stellt sicher, dass Änderungen an gemeinsam genutzten Variablen sich gegenseitig ausschließen, d. h. nur ein Thread wird gleichzeitig ausgeführt. Wenn es um gegenseitigen Ausschluss geht, denkt meiner Meinung nach jeder an

Sperre

. Ja, unser Thema heute sind Schlösser! Schlösser sollen das Atomizitätsproblem lösen.

Lock

Wenn es um Sperren geht, ist die erste Reaktion von Java-Studenten das synchronisierte Schlüsselwort. Schließlich wird es auf Sprachebene unterstützt. Werfen wir zunächst einen Blick auf die Synchronisierung. Einige Schüler verstehen die Synchronisierung nicht gut, daher gibt es viele Fallstricke bei der Verwendung.

synchronisierte Notizen

Schauen wir uns zunächst einen Code an. Dieser Code ist unser Weg, um die Löhne zu erhöhen. Und ein Thread vergleicht immer, ob unsere Löhne gleich sind. Lassen Sie mich kurz sagen: IntStream.rangeClosed(1,1000000).forEach,可能有些人对这个不太熟悉,这个代码的就等于 for 循环了100W次。

Haben Sie das richtige Schloss verwendet? Eine kurze Diskussion über Java-„Sperren“-Angelegenheiten

你先自己理解下,看看觉得有没有什么问题?第一反应好像没问题,你看着涨工资就一个线程执行着,这比工资也没有修改值,看起来好像没啥毛病?没有啥并发资源的竞争,也用 volatile 修饰了保证了可见性。

让我们来看一下结果,我截取了一部分。

Haben Sie das richtige Schloss verwendet? Eine kurze Diskussion über Java-„Sperren“-Angelegenheiten

可以看到首先有 log 打出来就已经不对了,其次打出来的值竟然还相等!有没有出乎你的意料之外?有同学可能下意识就想到这就raiseSalary在修改,所以肯定是线程安全问题来给raiseSalary 加个锁!

请注意只有一个线程在调用raiseSalary方法,所以单给raiseSalary

Sie sollten es zuerst selbst verstehen und sehen, ob Sie es spüren Was ist das Problem? Die erste Reaktion scheint in Ordnung zu sein. Wenn Sie sich die Gehaltserhöhung ansehen, ändert sich dadurch der Wert des Gehalts nicht. Es gibt keine Konkurrenz um gleichzeitige Ressourcen und es ist mit Volatile dekoriert, um die Sichtbarkeit zu gewährleisten. 🎜🎜Schauen wir uns die Ergebnisse an, ich habe einen Screenshot gemacht. 🎜
🎜Sie können sehen, dass das Protokoll zuerst gedruckt wird, und das ist es auch schon falsch. Dann sind die gedruckten Werte tatsächlich gleich! Hat Sie etwas überrascht? Einige Schüler denken möglicherweise unbewusst an Folgendes: raiseSalary Füge eine Sperre hinzu! 🎜🎜Bitte beachten Sie, dass es nur einen Thread gibt, der raiseSalary-Methode, also geben Sie einfach raiseSalary Methodensperre ist nutzlos. 🎜

Dies ist tatsächlich das Atomizitätsproblem, das ich oben erwähnt habe. Stellen Sie sich vor, dass die Ausführung des Threads zur Gehaltserhöhung abgeschlossen ist -right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: „Operator Mono“, Consolas, Monaco, Menlo, monospace;word-break: break -all ;color: rgb(60, 112, 198);">yesSalary++wurde noch nicht ausgeführtyourSalary++, der Gehaltsthread wird einfach ausgeführt bisyesSalary != yourSalary Ist es definitiv wahr? Deshalb wird das Protokoll gedruckt. yesSalary++还未执行yourSalary++时,比工资线程刚好执行到yesSalary != yourSalary 是不是肯定是 true ?所以才会打印出 log。

再者由于用 volatile 修饰保证了可见性,所以当打 log 的时候,可能yourSalary++已经执行完了,这时候打出来的 log 才会是yesSalary == yourSalary

所以最简单的解决办法就是把raiseSalary()compareSalary() 都用 synchronized 修饰,这样涨工资和比工资两个线程就不会在同一时刻执行,因此肯定就安全了!

Haben Sie das richtige Schloss verwendet? Eine kurze Diskussion über Java-„Sperren“-Angelegenheiten

看起来锁好像也挺简单,不过这个 synchronized 的使用还是对于新手来说还是有坑的,就是你要关注 synchronized 锁的究竟是什么。

比如我改成多线程来涨工资。这里再提一下parallel

Da darüber hinaus flüchtige Dekoration verwendet wird, um die Sichtbarkeit sicherzustellen, kann es bei der Protokollierung zu yourSalary++ wurde ausgeführt und die Protokollausgabe lautet zu diesem Zeitpunkt yesSalary == yourSalary. 🎜🎜Die einfachste Lösung besteht also darin, raiseSalary() und compareSalary() werden alle mit synchronisiert geändert, sodass die beiden Threads der Gehaltserhöhung und des Gehaltsvergleichs nicht gleichzeitig ausgeführt werden, was definitiv sicher ist! 🎜
🎜Es scheint, dass das Schloss recht einfach ist, aber das ist es synchronisiert Es gibt immer noch Fallstricke für Anfänger bei der Verwendung, das heißt, Sie müssen darauf achten, was synchronisierte Sperren sind. 🎜🎜Zum Beispiel bin ich auf Multi-Threading umgestiegen, um mein Gehalt zu erhöhen. Lassen Sie mich noch einmal erwähnenHaben Sie das richtige Schloss verwendet? Eine kurze Diskussion über Java-„Sperren“-Angelegenheiten

Aufgrund der raiseSalary() 加了锁,所以最终的结果是对的。这是因为 synchronized 修饰的是yesLockDemo-Instanz gibt es in unserer Hauptinstanz nur eine Instanz. Multithreads konkurrieren also um eine Sperre, sodass die endgültig berechneten Daten korrekt sind.

Dann werde ich den Code so ändern, dass jeder Thread seine eigene YesLockDemo-Instanz hat, um das Gehalt zu erhöhen.

Haben Sie das richtige Schloss verwendet? Eine kurze Diskussion über Java-„Sperren“-Angelegenheiten

Du wirst herausfinden, warum dieses Schloss nutzlos ist? Das versprochene Jahresgehalt von einer Million wird auf 100.000 geändert? ? Zum Glück hast du noch 70W.

Das liegt daran, dass unsere Sperre zu diesem Zeitpunkt eine nicht statische Methode ändert, bei der es sich um eine Sperre auf Instanzebene handelt, und wir für jeden Thread eine Instanz erstellt haben, sodass diese Threads überhaupt nicht um eine Sperre konkurrieren. , und der korrekte Code für die Multi-Thread-Berechnung oben ist, dass jeder Thread dieselbe Instanz verwendet und somit um eine Sperre konkurriert. Wenn Sie möchten, dass der Code zu diesem Zeitpunkt korrekt ist, müssen Sie nur die Sperre auf Instanzebene in eine Sperre auf Klassenebene ändern.

Es ist ganz einfach, diese Methode in eine statische Methode umzuwandeln. Das synchronisierte Ändern der statischen Methode ist eine Sperre auf Klassenebene.

Haben Sie das richtige Schloss verwendet? Eine kurze Diskussion über Java-„Sperren“-Angelegenheiten
Eine andere Möglichkeit besteht darin, eine statische Variable zu deklarieren. Dies ist empfehlenswerter, da die Umwandlung einer nicht statischen Methode in eine statische Methode tatsächlich einer Änderung der Codestruktur entspricht.

Haben Sie das richtige Schloss verwendet? Eine kurze Diskussion über Java-„Sperren“-Angelegenheiten
Wenn Sie synchronisiert verwenden, müssen Sie darauf achten, was die Sperre ist.

Wenn Sie statische Felder und statische Methoden ändern, handelt es sich um eine Sperre auf Klassenebene -statische Methoden, es handelt sich um eine Sperre auf Instanzebene .

Granularität sperren

Ich glaube, jeder weiß, dass Hashtable nicht empfohlen wird, wenn Sie es verwenden möchten. Dies liegt daran, dass Hashtable zwar threadsicher ist, aber zu grob ist auf die gleiche Weise. Werfen wir einen Blick auf den Quellcode.

Haben Sie das richtige Schloss verwendet? Eine kurze Diskussion über Java-„Sperren“-Angelegenheiten

Glauben Sie, dass dies etwas mit der Größenmethode zu tun hat? Warum darf ich die Größe nicht anpassen, wenn ich „Contains“ aufrufe? Dies liegt daran, dass die Sperrgranularität zu grob ist. Wir müssen unterschiedliche Sperren verwenden, um die Parallelität unter Thread-Sicherheit zu verbessern.

Aber unterschiedliche Sperren für unterschiedliche Methoden reichen nicht aus, da einige Vorgänge in einer Methode manchmal tatsächlich threadsicher sind. Nur der Code, der den Wettbewerb um Rennressourcen beinhaltet, muss gesperrt werden. Insbesondere wenn der Code, für den keine Sperre erforderlich ist, sehr zeitaufwändig ist, belegt er die Sperre lange und andere Threads können nur in der Schlange stehen, wie zum Beispiel der folgende Code.

Haben Sie das richtige Schloss verwendet? Eine kurze Diskussion über Java-„Sperren“-Angelegenheiten

Natürlich ist der zweite Teil des Codes die normale Art, die Sperre zu verwenden, aber im normalen Geschäftscode ist er nicht so einfach auf einen Blick zu erkennen wie der in meinem Code angegebene Schlaf. Manchmal muss er geändert werden . Die Reihenfolge der Codeausführung usw. stellt sicher, dass die Sperrgranularität fein genug ist.

Manchmal müssen wir sicherstellen, dass die Sperre dick genug ist, aber dieser Teil der JVM wird erkannt und hilft uns bei der Optimierung, wie zum Beispiel der folgende Code.

Haben Sie das richtige Schloss verwendet? Eine kurze Diskussion über Java-„Sperren“-Angelegenheiten

Sie können sehen, dass die in einer Methode aufgerufene Logik durchlaufen wurde 加锁-执行A-解锁-加锁-执行B-解锁,很明显的可以看出其实只需要经历加锁-执行A-执行B-解锁.

Daher wird die JVM die Sperre während der Just-in-Time-Kompilierung vergröbern und den Umfang der Sperre erweitern, ähnlich der folgenden Situation.

Haben Sie das richtige Schloss verwendet? Eine kurze Diskussion über Java-„Sperren“-Angelegenheiten

Und die JVM verfügt auch über die Aktion „Sperrenbeseitigung“. Durch die Escape-Analyse wird festgestellt, dass das Instanzobjekt Thread-privat ist. Daher muss es threadsicher sein, sodass die Sperraktion im Objekt erfolgt ignoriert und direkt angerufen.

Lese-Schreib-Sperre

Die Lese-Schreib-Sperre ist das, was wir oben angegeben haben, um die Granularität der Sperre je nach Szenario zu reduzieren. Es teilt eine Sperre in eine Lese-Sperre und eine Schreib-Sperre auf, was besonders wichtig ist Geeignet für Situationen, in denen mehr gelesen und weniger geschrieben wird. Verwenden Sie , um beispielsweise selbst einen Cache zu implementieren.

ReentrantReadWriteLockLese-/Schreibsperreermöglicht mehreren Threads das gleichzeitige Lesen gemeinsam genutzter Variablen, aber die Schreibvorgänge schließen sich gegenseitig aus, d. h. Schreiben und Schreiben schließen sich gegenseitig aus Lesen und Schreiben schließen sich gegenseitig aus. Um es ganz klar auszudrücken: Beim Schreiben kann nur ein Thread schreiben, und andere Threads können weder lesen noch schreiben.

Schauen wir uns ein kleines Beispiel an, das auch ein kleines Detail enthält. Dieser Code dient dazu, das Lesen des Caches zu simulieren. Zuerst wird die Lesesperre verwendet, um die Daten aus dem Cache abzurufen Daten aus der Datenbank, und dann werden die Daten in den Cache gestopft und zurückgegeben.

Das kleine Detail hier ist, noch einmal zu urteilen
, und dann ist der Cache leer, sodass alle um die Schreibsperre konkurrieren. Am Ende erhält nur ein Thread zuerst die Schreibsperre und stopft dann die Daten in den Cache. Haben Sie das richtige Schloss verwendet? Eine kurze Diskussion über Java-„Sperren“-Angelegenheiten
Zu diesem Zeitpunkt erhalten die wartenden Threads schließlich nacheinander die Schreibsperre. Beim Erwerb der Schreibsperre befindet sich bereits ein Wert im Cache, sodass keine erneute Abfrage der Datenbank erforderlich ist.

Natürlich kennt jeder das Verwendungsparadigma von Lock. Sie müssen try-finally um sicherzustellen, dass es entsperrt wird. Bei Lese-/Schreibsperren ist noch ein weiterer wichtiger Punkt zu beachten: try- finally,来保证一定会解锁。而读写锁还有一个要点需要注意,也就是说锁不能升级。什么意思呢?我改一下上面的代码。

Haben Sie das richtige Schloss verwendet? Eine kurze Diskussion über Java-„Sperren“-Angelegenheiten

但是写锁内可以再用读锁,来实现锁的降级,有些人可能会问了这写锁都加了还要什么读锁。

还是有点用处的,比如某个线程抢到了写锁,在写的动作要完毕的时候加上读锁,接着释放了写锁,此时它还持有读锁可以保证能马上使用写锁操作完的数据,而别的线程也因为此时写锁已经没了也能读数据

其实就是当前已经不需要写锁这种比较霸道的锁!所以来降个级让大家都能读。

小结一下,读写锁适用于读多写少的情况,无法升级,但是可以降级。Lock 的锁需要配合 try- finallySperren können nicht aktualisiert werden

. Was bedeutet es? Lassen Sie mich den obigen Code ändern.

Aber die Lesesperre kann innerhalb der Schreibsperre wieder verwendet werden . Um ein Sperren-Downgrade zu realisieren, fragen sich einige Leute möglicherweise, warum Lesesperren erforderlich sind, nachdem Schreibsperren hinzugefügt wurden.

Es ist immer noch einigermaßen nützlich, wenn ein Thread die Schreibsperre aktiviert, eine Lesesperre hinzufügt, wenn der Schreibvorgang kurz vor dem Abschluss steht, und dann die Schreibsperre aufhebt.

Zu diesem Zeitpunkt bleibt die Lesesperre erhalten um sicherzustellen, dass der Schreibvorgang sofort verwendet werden kann. Nach dem Sperren der Daten können auch andere Threads die Daten lesen, da die Schreibsperre zu diesem Zeitpunkt aufgehoben ist.

Tatsächlich besteht keine Notwendigkeit für aufdringlichere Sperren wie Schreibsperren! Also lasst es uns herunterstufen, damit jeder es lesen kann.

Zusammenfassend lässt sich sagen, dass die Lese-/Schreibsperre für Situationen geeignet ist, in denen mehr gelesen und weniger geschrieben wird. Sie kann nicht aktualisiert, aber herabgestuft werden. Die Sperre muss mit Lassen Sie mich übrigens kurz die Implementierung der Lese-/Schreibsperre erwähnen. Schüler, die mit AQS vertraut sind, kennen möglicherweise den Status im Inneren. Die Lese-/Schreibsperre teilt den int-Typstatus in zwei Hälften, die hohen 16 Bits und die unteren 16 Bits zeichnen den Status von Lesesperren bzw. Schreibsperren auf. Der Unterschied zu gewöhnlichen Mutex-Sperren besteht darin, dass diese beiden Zustände beibehalten werden und diese beiden Sperren in der Warteschlange unterschiedlich gehandhabt werden. AlsoIn Szenarien, die nicht für Lese-/Schreibsperren geeignet sind, ist es besser, Mutex-Sperren direkt zu verwenden

, da Lese-/Schreibsperren auch eine Verschiebungsbeurteilung des Zustands und anderer Vorgänge durchführen müssen.

🎜StampedLock🎜🎜🎜🎜🎜Ich möchte dieses Ding auch ein wenig erwähnen, es ist 1,8 und seine Auftrittsrate scheint nicht so hoch zu sein wie ReentrantReadWriteLock. Es unterstützt Schreibsperren, pessimistische Lesesperren und optimistische Lesevorgänge. Schreibsperren und pessimistische Lesesperren sind tatsächlich dieselben wie die Lese-/Schreibsperren in ReentrantReadWriteLock, die über einen zusätzlichen optimistischen Lesezugriff verfügen. 🎜🎜Aus der obigen Analyse wissen wir, dass die Lese-/Schreibsperre beim Lesen tatsächlich nicht schreiben kann, 🎜und das optimistische Lesen von StampedLock das Schreiben eines Threads ermöglicht🎜. Das optimistische Lesen ist tatsächlich dasselbe wie das uns bekannte optimistische Sperren der Datenbank. Das optimistische Sperren der Datenbank wird anhand eines Versionsfelds wie der folgenden SQL beurteilt. 🎜
Haben Sie das richtige Schloss verwendet? Eine kurze Diskussion über Java-„Sperren“-Angelegenheiten

StampedLock Optimistic Reading ähnelt dem, werfen wir einen Blick auf die einfache Verwendung.

Haben Sie das richtige Schloss verwendet? Eine kurze Diskussion über Java-„Sperren“-Angelegenheiten

Hier ist es im Vergleich zu ReentrantReadWriteLock. Andere sind nicht gut, zum Beispiel unterstützt StampedLock keine Wiedereintrittsmöglichkeit und keine Bedingungsvariablen. Ein weiterer Punkt ist, dass Sie bei Verwendung von StampedLock keine Interrupt-Operationen aufrufen dürfen, da dies dazu führt, dass die CPU 100 % auslastet. Ich habe das auf der Concurrent Programming-Website bereitgestellte Beispiel ausgeführt und reproduziert.

Haben Sie das richtige Schloss verwendet? Eine kurze Diskussion über Java-„Sperren“-Angelegenheiten

Die konkreten Gründe werden hier nicht näher erläutert. Am Ende des Artikels wird ein Link veröffentlicht.

Wenn also etwas erscheint, das mächtig zu sein scheint, muss man es wirklich verstehen und damit vertraut sein, um gezielt angreifen zu können.

CopyOnWrite

Copy-on-Write wird auch an vielen Stellen verwendet, z. Radius: 4px;Rand-rechts: 2px;Rand-links: 2px;Hintergrundfarbe: rgba(27, 31, 35, 0,05);Schriftfamilie: „Operator Mono“, Consolas, Monaco, Menlo, Monospace;word- break : break-all;color: rgb(60, 112, 198);">fork()

Operation. Es ist auch für unsere Geschäftscodeebene sehr hilfreich, da seine Lesevorgänge keine Schreibvorgänge blockieren und Schreibvorgänge keine Lesevorgänge blockieren. Geeignet für Szenarien, in denen viel gelesen und wenig geschrieben wird. fork() 操作。对于我们业务代码层面而言也是很有帮助的,在于它的读操作不会阻塞写,写操作也不会阻塞读。适用于读多写少的场景。

例如 Java 中的实现 CopyOnWriteArrayList,有人可能一听,这玩意线程安全读的时候还不会阻塞写,好家伙就用它了!

你得先搞清楚,写时复制是会拷贝一份数据,你的任何一个修改动作在CopyOnWriteArrayList 中都会触发一次Arrays.copyOf

Zum Beispiel die Implementierung in JavaCopyOnWriteArrayList, einige Leute hören vielleicht, dass dieses Ding das Schreiben nicht blockiert, wenn es threadsicher ist, also verwenden Sie es! 🎜🎜Sie müssen zunächst verstehen, dass 🎜Copy-on-Write eine Kopie der Daten kopiert🎜 Alle von Ihnen vorgenommenen Änderungen erfolgen in CopyOnWriteArrayList wird einmal ausgelöstArrays.copyOf und ändern Sie dann die Kopie. Wenn es viele Änderungsaktionen gibt und die kopierten Daten auch groß sind, wird dies eine Katastrophe sein! 🎜

Concurrent Safety Container

Lassen Sie uns abschließend über die Verwendung gleichzeitiger Sicherheitscontainer sprechen. Als Beispiel nehme ich die relativ bekannte ConcurrentHashMap. Ich denke, neue Kollegen scheinen zu glauben, dass sie Thread-sicher sein müssen, solange sie gleichzeitige Sicherheitscontainer verwenden. Tatsächlich kommt es nicht unbedingt darauf an, wie man es verwendet.

Schauen wir uns zunächst den folgenden Code an. Einfach ausgedrückt: Er verwendet ConcurrentHashMap, um das Gehalt aller bis zu 100 aufzuzeichnen.

Haben Sie das richtige Schloss verwendet? Eine kurze Diskussion über Java-„Sperren“-Angelegenheiten

Das Endergebnis wird den Standard übertreffen, das heißt, es sind nicht nur 100 Personen auf der Karte erfasst. Wie kann das Ergebnis also korrekt sein? Es ist so einfach wie das Hinzufügen einer Sperre.

Haben Sie das richtige Schloss verwendet? Eine kurze Diskussion über Java-„Sperren“-Angelegenheiten

Nachdem jemand das gesehen hatte, sagte er: Warum sollte ich ConcurrentHashMap verwenden, wenn Sie es bereits gesperrt haben? Ich kann einfach eine Sperre zu HashMap hinzufügen und es ist in Ordnung! Ja, du hast recht! Da es sich bei unserem aktuellen Verwendungsszenario um eine zusammengesetzte Operation handelt, beurteilen wir zunächst die Größe der Karte und führen dann die Put-Methode aus. kann nicht garantieren, dass zusammengesetzte Operationen threadsicher sind !

Und ConcurrentHashMap ist nur für die Verwendung geeignet Es dient dazu, threadsichere Methoden verfügbar zu machen, und nicht bei zusammengesetzten Vorgängen. Zum Beispiel der folgende Code

Haben Sie das richtige Schloss verwendet? Eine kurze Diskussion über Java-„Sperren“-Angelegenheiten

Natürlich ist mein Beispiel nicht angemessen genug. Tatsächlich liegt der Grund, warum ConcurrentHashMap eine höhere Leistung als HashMap + Lock aufweist, in der segmentierten Sperre, die die Reflexion mehrerer Tastenvorgänge erfordert. Der entscheidende Punkt, den ich jedoch hervorheben möchte, ist die Verwendung von Sie können nicht nachlässig sein und nicht einfach glauben, dass die Verwendung es threadsicher macht.

Zusammenfassend

Heute haben wir über die Ursachen von Parallelitätsfehlern gesprochen, nämlich über drei Hauptprobleme: Sichtbarkeitsprobleme, Atomizitätsprobleme und Ordnungsprobleme. Dann habe ich kurz über die wichtigsten Punkte des synchronisierten Schlüsselworts gesprochen, dh das Ändern statischer Felder oder statischer Methoden ist eine Sperre auf Klassenebene, während das Ändern nicht statischer Felder und nicht statischer Methoden eine Klasse auf Instanzebene ist.

Lassen Sie uns über die Granularität von Sperren in verschiedenen Szenarien sprechen, die nicht mit nur einer Sperre möglich sind, und die Granularität der internen Sperren der Methode muss in Ordnung sein. In Szenarien, in denen viel gelesen und viel geschrieben wird, können Sie beispielsweise Lese-/Schreibsperren, Copy-on-Write usw. verwenden.

Letztendlich müssen wir gleichzeitige Sicherheitscontainer korrekt verwenden. Wir können nicht blind glauben, dass die Verwendung gleichzeitiger Sicherheitscontainer Thread-sicher sein muss. Wir müssen auf das Szenario zusammengesetzter Operationen achten.

Haben Sie das richtige Schloss verwendet? Eine kurze Diskussion über Java-„Sperren“-Angelegenheiten

Natürlich habe ich heute nur kurz darüber gesprochen. Es gibt tatsächlich viele Punkte zur gleichzeitigen Programmierung. Es ist nicht einfach, threadsicheren Code zu schreiben, genau wie der gesamte Prozess der Kafka-Ereignisverarbeitung. In der Originalversion drehte sich alles um Parallelität und Sicherheit, die durch verschiedene Sperren kontrolliert wurden. Später konnten Fehler überhaupt nicht behoben werden, das Debuggen war schwierig und auch die Fehlerbehebung war schwierig.

Deshalb wurde das Kafka-Ereignisverarbeitungsmodul schließlich auf den Single-Threaded-Ereigniswarteschlangenmodus umgestellt, der den Zugriff im Zusammenhang mit der Konkurrenz gemeinsamer Daten in Ereignisse abstrahiert, die Ereignisse in die Blockierungswarteschlange stopft und sie dann in einem einzelnen Thread verarbeitet .

Bevor wir also ein Schloss verwenden, müssen wir darüber nachdenken, ob es notwendig ist? Kann man es vereinfachen? Andernfalls werden Sie später wissen, wie schmerzhaft es sein wird, es aufrechtzuerhalten.

Das obige ist der detaillierte Inhalt vonHaben Sie das richtige Schloss verwendet? Eine kurze Diskussion über Java-„Sperren“-Angelegenheiten. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:Java学习指南. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen