J’ai rencontré le code suivant dans notre projet:
synchronized (Thread.currentThread()){ //some code }
Je ne comprends pas la raison d’utiliser synchronisé sur currentThread
.
Y a-t-il une différence entre
synchronized (Thread.currentThread()){ //some code }
et juste
//some code
Pouvez-vous donner un exemple qui montre la différence?
plus en détail ce code comme suit:
synchronized (Thread.currentThread()) { Thread.currentThread().wait(timeInterval); }
Cela ressemble à Thread.sleep(timeInterval)
. Est-ce la vérité?
considère ceci
Thread t = new Thread() { public void run() { // A synchronized (Thread.currentThread()) { System.out.println("A"); try { Thread.sleep(5000); } catch (InterruptedException e) { } } } }; t.start(); synchronized (t) { // B System.out.println("B"); Thread.sleep(5000); }
les blocs A et B ne peuvent pas être exécutés simultanément, de sorte que dans le test donné, la sortie “A” ou “B” sera retardée de 5 secondes, la première étant indéfinie
Bien que ce soit presque un anti-modèle et qu’il devrait être résolu différemment, votre question immédiate appelle toujours une réponse. Si votre base de code entière n’obtient jamais un verrou sur une instance Thread
autre que Thread.currentThread()
, alors ce verrou ne sera jamais contesté. Cependant, si vous avez ailleurs
synchronized (someSpecificThreadInstance) { ... }
alors un tel bloc devra composer avec le bloc montré pour le même verrou. Il peut en effet arriver que le thread atteignant la synchronized (Thread.currentThread())
doit attendre qu’un autre thread abandonne le verrou.
Fondamentalement, il n’y a pas de différence entre la présence et l’absence du bloc synchronized
. Cependant, je peux penser à une situation qui pourrait donner un autre sens à cet usage.
Les blocs synchronized
ont un effet secondaire intéressant: ils créent une barrière de mémoire créée par le runtime avant d’entrer et après la sortie du bloc. Une barrière de mémoire est une instruction spéciale adressée à la CPU qui applique toutes les variables partagées entre plusieurs threads pour renvoyer leurs dernières valeurs. En général, un thread fonctionne avec sa propre copie d’une variable partagée et sa valeur est visible uniquement pour ce thread. Une barrière de mémoire indique au thread de mettre à jour la valeur de manière à ce que le changement soit visible pour les autres threads.
Donc, le bloc synchronisé dans ce cas ne fait pas de locking (car il n’y aura pas de cas réel de situation de locking et d’attente, à moins que je ne puisse penser) (sauf si le cas d’utilisation mentionné dans cette réponse est résolu) au lieu de cela, il applique les valeurs des champs partagés pour renvoyer leur dernière valeur. Ceci est cependant vrai si les autres endroits du code qui fonctionnent avec les variables en question utilisent également des barrières de mémoire (comme avoir le même bloc synchronized
autour des opérations de mise à jour / réaffectation). Cependant, ce n’est pas une solution pour éviter les conditions de course.
Si cela vous intéresse, je vous recommande de lire cet article . Il s’agit de barrières mémoire et de locking dans C # et le framework .NET, mais le problème est similaire pour Java et la JVM (sauf pour le comportement des champs volatils). Cela m’a beaucoup aidé à comprendre comment fonctionnent les threads, les champs volatiles et les verrous en général.
On doit tenir compte de certaines considérations sérieuses dans cette approche, qui ont été mentionnées dans les commentaires ci-dessous.
Vous implémentez un mutex récursif .
c’est-à-dire que le même thread peut entrer dans le bloc de synchronisation, mais pas les autres threads.