Les types de données primitifs comme int
& short
sont-ils thread-safe en Java? J'ai exécuté le code suivant et je n'ai pas pu voir le résultat attendu 500 fois.
public class SampleThree extends Thread
{
static long wakeUpTime = System.currentTimeMillis() + (1000*20);
static int inT;
public static void main(String args[])
{
System.out.println("initial:" + inT);
for(int i=0; i<500; i++)
new SampleThree().start();
try {
Thread.sleep(wakeUpTime - System.currentTimeMillis() + (1000*30));
System.out.println("o/p:" + inT);
}
catch(Exception e){
e.printStackTrace();
}
}
public void run()
{
try {
long s = wakeUpTime - System.currentTimeMillis();
System.out.println("will sleep ms: " + s);
Thread.sleep(s);
inT++; // System.out.println(inT);
}
catch(Exception e) {
e.printStackTrace();
}
}
}
Ici, 500 threads mettront à jour simultanément la variable int inT
. Le thread principal après avoir attendu la fin de la mise à jour simultanée, imprime la valeur inT
.
Trouver un exemple similaire ici
Ils ne sont pas sûrs de trois manières:
long
et double
ne sont même pas garantis d'être mis à jour atomiquement (vous pouvez voir la moitié d'une écriture à partir d'un thread différent)Utilisez AtomicInteger
etc pour les opérations thread-safe.
Les types primitifs ne sont pas thread-safe. Vérifiez ce tutoriel.
Je suggère d'utiliser des classes dans Java.util.concurrent.atomic. Ils sont conçus pour la sécurité des threads et, dans certains cas, la JVM peut tirer parti des fonctionnalités matérielles pour être optimisée.
Pour lire/écrire une valeur dans un environnement à plusieurs threads, le programme doit avoir une synchronisation ou un verrouillage approprié pour empêcher les courses de données. Cela n'a rien à voir avec le type de données auquel accéder. Dans un monde idéal, nous ne devrions rien partager ou seulement partager des objets immuables, ce qui est toujours sûr pour les threads.
En théorie, il n'est même pas garanti d'être atomique pour long/double selon https://docs.Oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.7 CEPENDANT, l'implémentation a tendance à être atomique, le code suivant n'imprime rien avec ou sans mot clé volatile dans mon environnement (64bit ubuntu 18.04, Intel 64bit CPU, Oracle JDK 8), donc c'est atomique dans cette situation, ce qui je suppose s'appliquent à tous les processeurs Intel/AMD 64. Nous pourrions également faire la même chose pour le double, bien qu'il soit un peu difficile de construire une valeur double avec certaines propriétés à vérifier.
public class LongThreadSafe {
// multiple threads read and write this value.
// according to the Java spec, only volatile long is guaranteed to be atomic
private static long value = 0;
private static final int max = (1 << 30) - 1;
private static final int threadCount = 4;
static ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
static CyclicBarrier barrier = new CyclicBarrier(threadCount);
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < threadCount; i++) {
executorService.submit(() -> {
try {
// all threads start to work at the same time
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
for (int j = 1; j < max; j++) {
// read value into v2
long v2 = value;
// check v2
int low = (int) v2;
int high = (int) (v2 >> 32);
if ((high << 1) != low) {
System.out.println("invalid number found high=" + high + ", low=" + low);
}
// write LongThreadSafe.value again
LongThreadSafe.value = ((long) j << 32) | (long) (j << 1);
}
});
}
executorService.shutdown();
executorService.awaitTermination(10, TimeUnit.MINUTES);
}
}