Ich habe einige Details zur Implementierung von ReentrantLock in "Java Concurrency in Practice" in Abschnitt 14.6.1 gelesen und etwas in der Anmerkung verwirrt mich:
Da die Methoden zur Manipulation des geschützten Zustands die Speichersemantik eines flüchtigen Lese- oder Schreibvorgangs haben, ist ReentrantLock darauf bedacht, das Besitzerfeld erst nach dem Aufruf von getState und nur vor dem Aufruf von setState zu lesen kann ReentrantLock die Speichersemantik des Synchronisationszustands übernehmen und somit weitere Synchronisation vermeiden, siehe Abschnitt 16.1.4.
der Code, auf den es sich bezieht:
%Vor% Und ich glaube, das ist der vereinfachte Code von nonfirTryAcquire
in ReentrantLock.Sync
.
Der verwirrende Teil ist also, wie die Einstellung von owner
, die lediglich eine einfache Instanzvariable in AbstractOwnableSynchronizer
ist, für else if (current == owner)
in anderen Threads sichtbar wird. Tatsächlich ist das Lesen von owner
nach dem Aufruf von getState()
(und state
ist eine volatile
qualifizierte Variable von AQS
), aber nach der Einstellung von owner
gibt es nichts (kann eine Synchronisation erzwingen) Semantik) überhaupt. Data Race passiert?
Nun, angesichts der Autorität dieses Buches und des gründlich getesteten Codes kommen mir zwei Möglichkeiten in den Sinn:
Die vollständige Barriere (sei es mfence oder 'lock'ed instruction) vor der Einstellung owner = current
macht die versteckte Arbeit. Aber nachdem ich von mehreren berühmten Artikeln gelernt habe, kümmert sich die volle Schranke mehr um die davor geschriebenen wie auch die darauf folgenden Lesevorgänge. Nun, wenn diese Möglichkeit wahr ist, dann könnten einige Sätze in "JCIP" unangemessen angegeben werden.
Ich stelle fest, dass ' setState(c+1)
' geographisch nach owner = current
im Code-Snippet kommt, obwohl es sich in einem anderen Zweig von if-else befindet. Wenn das, was die Kommentare sagen, die Wahrheit ist, bedeutet das, dass die von setSate(c+1)
eingefügte Barriere Synchronisationssemantik auf owner = current
in einem anderen Zweig auferlegen kann?
Ich bin ein Neuling in diesem Bereich, und einige großartige Blogs helfen mir sehr dabei, zu verstehen, was der JVM zugrunde liegt (keine Bestellung):
sowie die immer großartig: Ссылка
Nachdem ich meine Hausaufgaben gemacht und im Internet gesucht habe, komme ich nicht zu einem befriedigenden Ergebnis.
Entschuldigen Sie, wenn das zu wortreich oder unklar ist (Englisch ist nicht meine Muttersprache). Bitte helfen Sie mir dabei, alles, was damit zu tun hat, wird geschätzt.
Sie vermuten, dass zwischen owner = current;
(nach dem CAS) und if (current == owner)
(nach dem Lesen des Zustands und Überprüfen, ob es & gt; 0 ist) ein Wettlauf stattfinden könnte.
Wenn Sie dieses Stück Code isoliert betrachten, denke ich, dass Ihre Argumentation richtig ist. Sie müssen jedoch auch tryRelease
berücksichtigen:
Hier wird der Besitzer auf null
gesetzt, bevor der Status auf 0 gesetzt wird. Um die Sperre zu erhalten, muss der Status 0 sein und der Besitzer ist also null
.
Folglich
if (current == owner)
mit c=1
erreicht,
null
angezeigt, was ebenfalls gut ist. if (current == owner)
mit c>1
erreicht,
Ich stimme zu, dass die Fußnote "lese das Besitzerfeld nur nach dem Aufruf von getState und schreibe es nur vor dem Aufruf von setState " in JCIP ist irreführend. Es schreibt den owner
vor dem Aufruf von setState
in tryRelease
, aber nicht tryAcquire
.
Dies wird in diesem Blogpost ziemlich gut erklärt. Die untere Zeile besagt, dass, wenn der Lese-Thread ein flüchtiges Feld liest, alle vom Schreib-Thread aktualisierten Felder, die vor dem Schreiben in das flüchtige Feld geändert wurden, auch für den Lese-Thread sichtbar sind. Die Sperrklasse organisiert die Feldzugriffe, um sicherzustellen, dass nur das Statusfeld flüchtig sein muss, und das Besitzerfeld wird bei Bedarf weiterhin sicher propagiert.
Tags und Links java multithreading synchronization concurrency memory-barriers