Maison  >  Article  >  Java  >  En savoir plus sur les trois éléments de la programmation simultanée Java

En savoir plus sur les trois éléments de la programmation simultanée Java

WBOY
WBOYavant
2022-04-22 11:54:482718parcourir

Cet article vous apporte des connaissances pertinentes sur java, qui présente principalement les problèmes liés aux trois éléments de la programmation simultanée, notamment l'atomicité, la visibilité, l'ordre, leurs causes et définitions, etc. Jetons un coup d'œil au contenu ci-dessous, j'espère. cela sera utile à tout le monde.

En savoir plus sur les trois éléments de la programmation simultanée Java

Étude recommandée : "Tutoriel vidéo Java"

1 Atomicité

1.1 La définition de l'atomicité

Atomique fait référence à une ou plusieurs opérations, soit toutes exécutées et ne soient pas interrompues par d'autres opérations pendant l'exécution, ou ne s'exécutent pas du tout.

1.2 Causes des problèmes d'atomicité

La commutation de thread est la cause des problèmes d'atomicité. La commutation de thread vise à améliorer l'utilisation du processeur.

En prenant count ++ comme exemple, au moins trois instructions CPU sont requises :

  • Instruction 1 : Tout d'abord, la variable count doit être chargée de la mémoire vers le registre CPU
  • Instruction 2 : Après cela, effectuez le Opération +1 dans le registre ;
  • Instruction 3 : Enfin, écrivez le résultat dans la mémoire (le mécanisme de mise en cache provoque l'écriture du cache CPU au lieu de la mémoire).

Nous supposons que count=0. Si le thread A change de thread après l'exécution de l'instruction 1 et que le thread A et le thread B s'exécutent selon la séquence de la figure ci-dessous, alors nous constaterons que les deux threads ont effectué l'opération count+=. 1, Mais le résultat obtenu n’est pas 2 comme on l’espérait, mais 1.

1.3 Opérations atomiques

Dans un environnement multithread, Java garantit uniquement que les variables et les opérations d'affectation des types de données de base sont atomiques (Remarque : dans un environnement JDK 32 bits, pour les données 64 bits, la lecture n'est pas une opération atomique*, comme long, double)

1.4 Comment résoudre le problème d'atomicité

Si nous pouvons garantir que les modifications apportées aux variables partagées s'excluent mutuellement, alors, qu'il s'agisse d'un processeur monocœur ou d'un CPU multicœur, cela garantira l'atomicité. Le verrouillage peut résoudre les problèmes d'atomicité, tels que l'utilisation de la synchronisation et du verrouillage.

2 Visibilité

2.1 Définition de la visibilité

La visibilité signifie que lorsque plusieurs threads exploitent une variable partagée, après qu'un thread ait modifié la variable, les autres threads peuvent immédiatement voir le résultat de la modification.

2.2 Causes des problèmes de visibilité

La cohérence des données entre le cache du processeur et la mémoire est la cause du problème de visibilité. Le cache du processeur vise à améliorer l'efficacité du processeur.

2.3 Résolution des problèmes de visibilité

La cause du problème de visibilité est le cache du processeur, nous pouvons alors désactiver le cache du processeur.

  • un champ volatile peut désactiver le cache du processeur et résoudre les problèmes de visibilité.
  • Les synchronisations et les verrouillages peuvent garantir la visibilité.

2.4 Qu'est-ce que la règle de visibilité ?

La règle de visibilité est la règle Happens-Before.

Règle "Happens-Before" :

  • En termes simples : Le résultat de l'opération précédente est visible pour les opérations suivantes.
  • Happens-Before restreint le comportement d'optimisation du compilateur Bien que le compilateur soit autorisé à optimiser, il nécessite que le compilateur se conforme aux règles Happens-Before après l'optimisation.

2.5 Règles qui se produisent avant

  • Règles de séquence de programme

Dans un fil de discussion, selon la séquence du programme, l'opération précédente se produit avant toute opération ultérieure.

class Example {
  public void test() {
    int x = 42;   ①
    int y = 20;   ②
  }
 
}

① Arrive avant ②.

  • règles de variable volatile

Une opération d'écriture sur une variable volatile se produira avant les opérations de lecture ultérieures sur cette variable volatile.

  • Règle de transitivité

Si A se produit avant B et que B se produit avant C, alors A se produit avant C.

class Example {
  int x = 0;
  volatile int y = 0;
  public void writer() {
    x = 42;      ①
    y = 1;       ②
  }
  public void reader() {
    if (y == 1) {  ③
      // 这里x会是多少呢?
    }
  }
}
  • ① Arrive-Avant ②, satisfaisant la règle 1-règle séquentielle.
  • ② Arrive-Avant ③, satisfaisant la règle 2-règle des variables volatiles.
  • ① Arrive-Avant ③, satisfaisant la règle 3-règle de transitivité. Si y == 1, alors x = 42 ;
  • Règles de verrouillage dans le moniteur

Le déverrouillage d'une serrure se produit avant et le verrouillage ultérieur de cette serrure.

管程是一种通用的同步原语,在 Java 中指的就是 synchronized,synchronized 是 Java 里对管程的实现。

synchronized (this) { //此处自动加锁
  // x是共享变量,初始值=10
  if (this.x < 12) {
    this.x = 12; 
  }  
} //此处自动解锁

假设 x 的初始值是 10,线程 A 执行完代码块后 x 的值会变成 12(执行完自动释放锁);

线程 B 进入代码块时,能够看到线程 A 对 x 的写操作,也就是线程 B 能够看到 x==12。

  • 线程 start() 规则

它是指主线程 A 启动子线程 B 后,子线程 B 能够看到主线程在启动子线程 B 前的操作。

  • 线程 join() 规则

它是指主线程 A 等待子线程 B 完成(主线程 A 通过调用子线程 B 的 join() 方法实现),当子线程 B 完成后(主线程 A 中 join() 方法返回),主线程能够看到子线程的操作。当然所谓的“看到”,指的是对共享变量的操作。

3 有序性

3.1 有序性的定义

有序性,即程序的执行顺序按照代码的先后顺序来执行。

3.2 有序性问题原因

编译器为了优化性能,有时候会改变程序中语句的先后顺序。

例如:“a=6;b=7;”编译器优化后可能变成“b=7;a=6;”,在这个例子中,编译器调整了语句的顺序,但是不影响程序的最终结果。

以双重检查代码为例:

public class Singleton {
  static Singleton instance;
  static Singleton getInstance(){
    if (instance == null) {    ①
      synchronized(Singleton.class) {
        if (instance == null)
          instance = new Singleton();  ②
        }
    }
    return instance;
  }
}

上面的代码有问题,问题在 ② 操作上:经过优化后的执行路径是这样的:

  1. 分配一块内存 M;
  2. 将 M 的地址赋值给 instance 变量;
  3. 最后在内存 M 上初始化 Singleton 对象。

优化后会导致什么问题呢?我们假设线程 A 先执行 getInstance() 方法,当执行完 ① 时恰好发生了线程切换,切换到了线程 B 上;如果此时线程 B 也执行 getInstance() 方法,那么线程 B 在执行第一个判断时会发现 instance != null ,所以直接返回 instance,而此时的 instance 是没有初始化过的,如果我们这个时候访问 instance 的成员变量就可能触发空指针异常。

如何解决双重检查问题?变量用 volatile 来修饰,禁止指令重排序

public class Singleton {
  static volatile Singleton instance;
  static Singleton getInstance(){
    if (instance == null) {    ①
      synchronized(Singleton.class) {
        if (instance == null)
          instance = new Singleton();  ②
        }
    }
    return instance;
  }
}

推荐学习:《java视频教程

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer