Comment garantir la sécurité des threads en Java?
Qu'est-ce que le thread safe?
Un état dans lequel la synchronisation est garantie lorsque des données variables sont partagées entre les threads.
Quelle est la garantie de synchronisation?
Défini comme satisfaisant les deux points «garantie d'atomicité» et «garantie de visibilité».
Quelle est la garantie d'atomicité?
Il est possible d'exclure les interférences externes du début à la fin d'un certain processus.
Quelle est la garantie de visibilité?
La dernière valeur peut être mentionnée pour la série chronologique.
Surtout en Java, la syntaxe de contrôle suivante est liée aux éléments qui sont apparus jusqu'à présent.
bloc synchronisé
Synchronisez les opérations entre les blocs qui spécifient le même objet comme argument.
La qualification synchronisée d'une méthode équivaut à un bloc synchronisé pour sa propre instance.
//Synchronisez les opérations entre les blocs synchronisés suivants
public void foo() {
synchronized (_SOME_OBJECT) {
// do something
}
}
public void bar() {
synchronized (_SOME_OBJECT) {
// do something
}
}
//Les éléments suivants sont équivalents
public void foo() {
synchronized (this) {
// do something
}
}
public void synchronized foo() {
// do something
}
modificateur volatil
Garantir la visibilité de la variable spécifiée.
Certaines situations peuvent être utilisées à des fins thread-safe, mais nous ne les traiterons pas cette fois.
Par exemple, les variables de type int et de type booléen sont garanties atomiques par les spécifications du langage.
Prenons le cas où foo () et bar () sont appelés à partir de différents threads plusieurs fois à des intervalles irréguliers pour l'implémentation suivante.
public void foo() {
_object.update(); // _Traitement pour changer l'état interne de l'objet
}
public void bar() {
if (_object.enabled()) {
_object.getData(); // enabled()Une exception se produit lorsqu'elle est appelée lorsque la valeur est false
}
}
Puisqu'il n'y a aucune garantie d'atomicité dans le traitement de bar (), les méthodes peuvent être appelées dans l'ordre suivant. _object.enabled() -> _object.update() -> _object.getData()
public void foo() {
_object.update();
}
public void bar() {
if (_object.enabled()) {
_object.getData(); // _L'état interne de l'objet n'est pas garanti
}
}
Une garantie de synchronisation est ajoutée lors du traitement dans le bloc, c'est-à-dire que l'atomicité est également garantie. 【 _object.update() 】 -> 【 _object.enabled() -> _object.getData() 】 -> 【 _object.update() 】
Il est facile de comprendre si une image est stockée dans la file d'attente de traitement en unités de bloc.
public void foo() {
synchronized (this) {
_object.update();
}
}
public void bar() {
synchronized (this) {
if (_object.enabled()) {
_object.getData(); // _L'état interne de l'objet est garanti
}
}
}
Synchronisez les opérations entre les blocs qui spécifient le même objet comme argument
Le bloc synchronisé n'est qu'une garantie de synchronisation comme décrit ci-dessus. L'implémentation suivante ne garantit pas la synchronisation du traitement à l'intérieur de bar ().
public void foo() {
_object.update();
}
public void bar() {
synchronized (this) {
if (_object.enabled()) {
_object.getData(); // _L'état interne de l'objet n'est pas garanti
}
}
}
Un supplément au cas où. La synchronisation n'est pas garantie pour les opérations entre des blocs qui spécifient différents objets comme arguments.
public void foo() {
synchronized (_SOME_OBJECT) {
_object.update();
}
}
public void bar() {
synchronized (this) {
if (_object.enabled()) {
_object.getData(); // _L'état interne de l'objet n'est pas garanti
}
}
}
Afin de minimiser l'occurrence de blocage, il est souhaitable d'appliquer les principes suivants à l'implémentation.
this
Non souhaitable pour les principes car il s'agit d'un objet public pour l'utilisateur
La méthode synchronisée équivalente ne doit pas non plus être utilisée
champ de cours privé
Garanti inaccessible aux utilisateurs
Non souhaitable pour les principes car il s'agit d'un objet partagé entre les instances
champ d'instance privée
Garanti inaccessible aux utilisateurs
Et comme il est généré pour chaque instance, il est optimal pour le principe
En outre, dans les deux cas, l'objet de verrouillage ne doit pas être nul. Ceci est garanti en ajoutant le modificateur final.
//Implémentation recommandée
class Shared {
private final Object _LOCK_OBJECT = new Object(); //Ne le rendez pas statique
public void foo() {
synchronized (_LOCK_OBJECT) {
// do something
}
}
public void bar() {
synchronized (_LOCK_OBJECT) {
// do something
}
}
}
class User {
public void sample() {
final Shared object = new Shared();
new Thread(() -> object.foo()).start();
new Thread(() -> object.bar()).start();
}
}
//Implémentations qui l'utilisent pour les objets de verrouillage
class Shared {
public void foo() {
synchronized (this) {
// do something
}
}
public void bar() {
synchronized (this) {
// do something
}
}
}
class User {
public void sample() {
final Shared object = new Shared();
//Cette instance d'objet sert de référence à l'objet Lock de l'implémentation interne Shared.
//Par conséquent, il viole le principe de "minimiser la plage de partage des objets de verrouillage".
}
}
De même, selon le principe de minimisation de la plage de partage, un objet de verrouillage doit être défini pour chaque cible pour laquelle la sécurité des threads doit être garantie.
class Shared {
//Verrouiller la cible
private final Data _dataA = new Data();
private final Data _dataB = new Data();
private final Data _dateC = new Data();
private final Validator = _validator = new Validator();
//Verrouiller l'objet
private final Object _DATA_A_LOCK = new Object();
private final Object _DATA_B_LOCK = new Object();
private final Object _DATA_C_LOCK = new Object();
public void foo() {
synchronized (_DATA_A_LOCK) {
_dataA.update();
}
synchronized (_DATA_B_LOCK) {
_dataB.update();
}
synchronized (_DATA_C_LOCK) {
_validator.execute();
_dataC.update();
}
}
public void bar() {
synchronized (_DATA_A_LOCK) {
_dataA.get();
}
synchronized (_DATA_B_LOCK) {
_dataB.get();
}
synchronized (_DATA_C_LOCK) {
_validator.execute();
_dataC.get();
}
}
}
Recommended Posts