在Java多线程中,i++和i--是非线程安全的。
例子:
public class PlusPlusTest {
public static void main(String[] args) throws InterruptedException {
Num num = new Num();
ThreadA threadA = new ThreadA(num);
ThreadB threadB = new ThreadB(num);
threadA.start();
threadB.start();
Thread.sleep(200);
System.out.println(num.count);
}
}
class ThreadA extends Thread {
private Num num;
public ThreadA(Num num) {
this.num = num;
}
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
num.count++;
}
}
}
class ThreadB extends Thread {
private Num num;
public ThreadB(Num num) {
this.num = num;
}
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
num.count++;
}
}
}
class Num {
int count = 0;
public Num() {
}
}
以上代码输出结果基本上不是2000,会比2000小。
原因:
在线程A中,i++的过程为:
temp1 = i; temp2 = temp1 + 1; i = temp2;
在线程B中,i++的过程为:
temp3 = i; temp4 = temp3 + 1; i = temp4;
在i=0的时候,线程A和B同时读取i=0。
线程A执行++后,i被修改成1。
线程B执行++后,i被修改,但还是1。
问:这样的解释对么?
想到把count变量申明为volatile,但是:
即使把count申明为volatile,输出的结果也不是2000,请问为什么?
class Num {
volatile int count = 0;
public Num() {
}
}
最后
把count变量包装成AtomicInteger之后,输出的结果为2000,正确,这又是为什么?
Parce que
volatile
ne garantit pas l'atomicité de l'opération,i++
cette opération n'est pas une opération atomique.n'est en fait pas très approprié. Le fonctionnement de
i++
ne devrait pas être si gênant. La valeur de lecture de fait référence à la lecture du CPU .Une erreur s'est produite, la séquence d'exécution est la suivante :
线程1
lit que la valeur dei
est0
,线程2
indique également que la valeur dei
est0
,线程1
a effectué l'opération+1
et a écrit la valeur du résultat1
dans la mémoire,线程2
effectue l'opération+1
et écrit la valeur du résultat1
dans la mémoire.volatile
ne peut garantir que la visibilité, ce qui signifie que la dernière valeur dei
peut être lue en temps réel, mais il ne peut pas garantir l'atomicité, c'est-à-dire que la séquence d'exécution ci-dessus est complètement autorisée à se produire. Vous pouvez également vous référer à ma réponse à cette question : https://segmentfault.com/q/10...
.AtomicInteger
est unint
atomique. Ceci est implémenté parJava
Comprenons grossièrement le code source :Le point clé est
compareAndSet()
. Cette méthode déterminera si les valeurs decurrent
eti
remplissent les conditions :此时i的值是否和current相等
Si les conditions sont remplies, quittez directement la boucle. ,再++
sera répété jusqu'à ce qu'il soit normal.compareAndSet方法
est implémenté parJava的unsafe
Cela devrait être de très bas niveau. Ce sont toutes des méthodesnative
, et je ne les ai pas étudiées. Cependant, le programmeur moyen ne sera pas exposé à launsafe
programmation.Volatile ne peut garantir que la visibilité, c'est-à-dire que vous pouvez le lire immédiatement après que quelqu'un d'autre l'a modifié, mais d'autres peuvent également le modifier lorsque vous le modifiez.
AtomicInteger est basé sur CAS (Compare And Swap).
Je suis trop paresseux pour écrire une réponse Voici un très bon article : Programmation simultanée en Java : analyse des mots clés volatiles
Volatile garantit que les données obtenues à chaque fois sont les plus récentes (lues depuis la mémoire), i++; --> i=i+1; Si i+1 est exécuté sans attribuer de valeur à i, il n'y a aucune garantie qu'une autre Les données obtenues par le thread sont les dernières, et cette dernière est une opération atomique, on peut donc garantir que i = this sera définitivement exécuté