Cela signifie-t-il que deux threads ne peuvent pas modifier les données sous-jacentes simultanément? Ou cela signifie-t-il que le segment de code donné fonctionnera avec des résultats prévisibles lorsque plusieurs threads l'exécutent?
De Wikipedia:
La sécurité des threads est un concept de programmation informatique applicable dans le contexte de programmes multithreads. Un morceau de code est thread-safe s'il fonctionne correctement lors de l'exécution simultanée par plusieurs threads. En particulier, il doit satisfaire le besoin de plusieurs threads d'accéder aux mêmes données partagées, ainsi que le besoin d'accéder à une donnée partagée par un seul thread à la fois.
Il existe plusieurs façons d’atteindre la sécurité des threads:
Réentrée:
L'écriture de code de telle sorte qu'il puisse être partiellement exécuté par une tâche, réintégrée par une autre tâche, puis reprise à partir de la tâche d'origine. Cela nécessite l'enregistrement des informations d'état dans des variables locales à chaque tâche, généralement sur sa pile, plutôt que dans des variables statiques ou globales.
Exclusion mutuelle:
L'accès aux données partagées est sérialisé à l'aide de mécanismes garantissant qu'un seul thread lise ou écrit les données partagées à tout moment. Il faut faire très attention si un élément de code accède à de multiples éléments de données partagés - les problèmes incluent les conditions de concurrence, des blocages, des blocages de vie, la famine et divers autres maux énumérés dans les manuels de nombreux systèmes d'exploitation.
Stockage local de threads:
Les variables sont localisées de sorte que chaque thread possède sa propre copie privée. Ces variables conservent leurs valeurs dans les limites du sous-programme et des autres codes, et sont sécurisées pour les threads car elles sont locales pour chaque thread, même si le code qui y accède peut être réentrant.
Opérations atomiques:
L'accès aux données partagées s'effectue à l'aide d'opérations atomiques qui ne peuvent pas être interrompues par d'autres threads. Cela nécessite généralement l'utilisation d'instructions spéciales en langage machine, qui peuvent être disponibles dans une bibliothèque d'exécution. Comme les opérations sont atomiques, les données partagées sont toujours conservées dans un état valide, quels que soient les autres threads qui y ont accès. Les opérations atomiques constituent la base de nombreux mécanismes de blocage de fil.
en savoir plus:
http://en.wikipedia.org/wiki/Thread_safety
en allemand: http://de.wikipedia.org/wiki/Threadsicherheit
en français: http://fr.wikipedia.org/wiki/Threadsafe (très court)
Le code Thread-safe est un code qui fonctionnera même si de nombreux threads l'exécutent simultanément.
Une question plus informative est ce qui rend le code non thread thread- et la réponse est qu'il y a quatre conditions qui doivent être vraies ... Imaginez le code suivant (et sa traduction en langage machine)
totalRequests = totalRequests + 1
MOV EAX, [totalRequests] // load memory for tot Requests into register
INC EAX // update register
MOV [totalRequests], EAX // store updated value back to memory
J'aime la définition de Java de Concurrence dans la pratique de Brian Goetz pour son exhaustivité
"Une classe est thread-safe si elle se comporte correctement lorsqu'elle est accédée à partir de plusieurs threads, indépendamment de la planification ou de l'entrelacement de l'exécution de ces threads par l'environnement d'exécution, et sans synchronisation ou autre coordination de la part du code appelant. "
Comme d'autres l'ont souligné, la sécurité des threads signifie qu'un morceau de code fonctionnera sans erreur s'il est utilisé par plusieurs threads à la fois.
Il faut savoir que cela a parfois un coût en temps d’ordinateur et en codage plus complexe, ce qui n’est pas toujours souhaitable. Si une classe ne peut être utilisée en toute sécurité que sur un seul thread, il peut être préférable de le faire.
Par exemple, Java a deux classes presque équivalentes, StringBuffer
et StringBuilder
. La différence est que StringBuffer
est thread-safe, de sorte qu'une seule instance de StringBuffer
peut être utilisée par plusieurs threads à la fois. StringBuilder
n'est pas thread-safe et est conçu pour remplacer de manière plus performante les cas (la grande majorité) lorsque String est construit par un seul thread.
Thread-safe-code fonctionne comme spécifié, même lorsqu'il est entré simultanément par différents threads. Cela signifie souvent que les structures de données internes ou les opérations devant être exécutées sans interruption sont protégées simultanément contre différentes modifications.
Un moyen plus simple de le comprendre est ce qui rend le code non thread-safe. Il y a deux problèmes principaux qui feront qu'une application à threads aura un comportement indésirable.
Accéder à une variable partagée sans verrouillage
Cette variable peut être modifiée par un autre thread lors de l'exécution de la fonction. Vous voulez éviter cela avec un mécanisme de verrouillage pour vous assurer du comportement de votre fonction. La règle générale est de garder le verrou le plus rapidement possible.
Blocage causé par la dépendance mutuelle à une variable partagée
Si vous avez deux variables partagées A et B. Dans une fonction, vous verrouillez A d'abord, puis vous verrouillez B. Dans une autre fonction, vous commencez à verrouiller B et au bout d'un moment, vous verrouillez A. C'est un potentiel impasse où la première fonction attendra que B soit déverrouillé alors que la deuxième fonction attendra que A soit déverrouillée Ce problème ne se produira probablement pas dans votre environnement de développement et uniquement de temps en temps. Pour l'éviter, tous les verrous doivent toujours être dans le même ordre.
Oui et non.
La sécurité des threads est un peu plus que de simplement s'assurer que vos données partagées ne sont accessibles que par un thread à la fois. Vous devez assurer un accès séquentiel aux données partagées, tout en évitant conditions de concurrence , blocages , livelocks , et ressource la famine .
Des résultats imprévisibles lorsque plusieurs threads sont en cours d'exécution ne sont pas une condition requise du code thread-safe, mais il s'agit souvent d'un sous-produit. Par exemple, vous pouvez avoir un schéma producteur-consommateur défini avec une file d'attente partagée, un thread producteur et peu de threads consommateurs, et le flux de données peut être parfaitement prévisible. Si vous commencez à attirer plus de consommateurs, vous obtiendrez des résultats plus aléatoires.
Simplement - le code fonctionnera correctement si plusieurs threads exécutent ce code en même temps.
En substance, de nombreux problèmes peuvent survenir dans un environnement multithread (réordonnancement d'instructions, objets partiellement construits, même variable ayant des valeurs différentes dans des threads différents en raison de la mise en cache au niveau de la CPU, etc.).
J'aime la définition donnée par Java Concurrency in Practice :
Une [partie de code] est thread-safe si elle se comporte correctement lorsqu'elle est accédée à partir de plusieurs threads, indépendamment de la planification ou de l'entrelacement de l'exécution de ces threads par l'environnement d'exécution, et sans synchronisation ou autre coordination supplémentaire de la part de l'environnement d'exécution. code d'appel.
Par correctement , cela signifie que le programme se comporte conformément à ses spécifications.
Exemple élaboré
Imaginez que vous implémentiez un compteur. Vous pourriez dire qu'il se comporte correctement si:
counter.next()
ne renvoie jamais une valeur qui a déjà été renvoyée auparavant (nous supposons qu'il n'y a pas de dépassement de capacité, etc. pour des raisons de simplicité)Un compteur de thread thread safe se comporterait selon ces règles quel que soit le nombre de threads y accédant simultanément (ce qui ne serait généralement pas le cas d’une implémentation naïve).
J'aimerais ajouter quelques informations supplémentaires à d'autres bonnes réponses.
La sécurité des threads implique que plusieurs threads puissent écrire/lire des données dans le même objet sans erreurs d'incohérence de la mémoire. Dans les programmes hautement multithreads, un programme thread-safe ne provoque pas d'effets secondaires sur les données partagées .
Regardez cette question SE pour plus de détails:
Qu'est-ce que threadsafe signifie?
Un programme thread-safe garantit la cohérence de la mémoire .
De la documentation Oracle page sur l'API simultanée avancée:
Propriétés de cohérence de la mémoire:
Le chapitre 17 de la spécification du langage Java ™ définit la relation passe-avant sur les opérations en mémoire, telles que la lecture et l'écriture de variables partagées. Les résultats d'une écriture par un thread sont visibles pour une lecture par un autre thread uniquement si l'opération d'écriture a lieu avant l'opération de lecture .
Les constructions synchronized
et volatile
, ainsi que les méthodes Thread.start()
et Thread.join()
, peuvent former des relations qui se passent avant .
Les méthodes de toutes les classes de Java.util.concurrent
et de ses sous-packages étendent ces garanties à la synchronisation de niveau supérieur. En particulier:
Runnable
à un Executor
happen-avant que son exécution ne commence. De même pour les Callables soumis à un ExecutorService
.Future
actions à réaliser après la récupération du résultat via Future.get()
dans un autre thread.Lock.unlock, Semaphore.release, and CountDownLatch.countDown
happen-before actions consécutives à une méthode "d'acquisition" réussie, telle que Lock.lock, Semaphore.acquire, Condition.await, and CountDownLatch.await
sur le même synchroniseur objet dans un autre thread.Exchanger
, les actions antérieures à la exchange()
de chaque thread ont lieu avant celles qui ont suivi l'échange () correspondant dans un autre thread.CyclicBarrier.await
et Phaser.awaitAdvance
(ainsi que ses variantes) se produisent avant les actions effectuées par l'action barrière et les actions effectuées par l'action barrière se produisent avant des actions consécutives à un retour réussi du correspondant attendent dans d'autres discussions.Ne confondez pas la sécurité du fil avec le déterminisme. Le code thread-safe peut également être non déterministe. Compte tenu de la difficulté des problèmes de débogage avec le code threadé, c'est probablement le cas normal. :-)
La sécurité des threads garantit simplement que lorsqu'un thread modifie ou lit des données partagées, aucun autre thread ne peut y accéder de manière à modifier les données. Si votre code dépend d'un certain ordre d'exécution pour être correct, vous avez besoin d'autres mécanismes de synchronisation que ceux requis pour la sécurité des threads.
Pour compléter d'autres réponses:
La synchronisation n'est un souci que lorsque le code de votre méthode effectue l'une des deux choses suivantes:
Cela signifie que les variables définies dans votre méthode sont toujours threadsafe. Chaque appel à une méthode a sa propre version de ces variables. Si la méthode est appelée par un autre thread, ou par le même thread, ou même si la méthode s'appelle elle-même (récursivité), les valeurs de ces variables ne sont pas partagées.
La planification des threads n'est pas garantie d'être round-robin. Une tâche peut totalement absorber le processeur aux dépens de threads de même priorité. Vous pouvez utiliser Thread.yield () pour avoir une conscience. Vous pouvez utiliser (en Java) Thread.setPriority (Thread.NORM_PRIORITY-1) pour réduire la priorité d'un thread.
De plus méfiez-vous de:
Oui et oui. Cela implique que les données ne sont pas modifiées simultanément par plusieurs threads. Cependant, votre programme peut fonctionner comme prévu et sembler thread-safe, même s'il ne l'est pas fondamentalement.
Notez que l'imprévisibilité des résultats est une conséquence des "conditions de concurrence" qui entraînent probablement une modification des données dans un ordre autre que celui attendu.
Répondons à cela par exemple:
class NonThreadSafe {
private int counter = 0;
public boolean countTo10() {
count = count + 1;
return (count == 10);
}
La méthode countTo10
ajoute un au compteur, puis renvoie true si le nombre a atteint 10. Elle ne doit renvoyer true qu'une seule fois.
Cela fonctionnera tant qu'un seul thread exécute le code. Si deux threads exécutent le code en même temps, divers problèmes peuvent survenir.
Par exemple, si le nombre commence à 9, un thread peut ajouter 1 (10), mais un deuxième thread peut entrer la méthode et ajouter 1 à nouveau (soit 11) avant que le premier ne puisse exécuter la comparaison avec 10 Ensuite, les deux threads font la comparaison et trouvent ce nombre égal à 11 et aucun des deux ne retourne vrai.
Donc, ce code n'est pas thread-safe.
En substance, tous les problèmes multi-threading sont causés par une variation de ce type de problème.
La solution consiste à s'assurer que l'addition et la comparaison ne peuvent pas être séparées (par exemple en entourant les deux instructions par un code de synchronisation) ou en concevant une solution ne nécessitant pas deux opérations. Un tel code serait thread-safe.
En termes simples: P S'il est sûr d'exécuter plusieurs threads sur un bloc de code, il est thread-safe *
* conditions applicables
Les conditions sont mentionnées par d'autres entités telles que 1. Le résultat doit être identique si vous exécutez un ou plusieurs threads dessus, etc.