web-dev-qa-db-fra.com

Les types de données primitifs sont-ils thread-safe dans Java

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

34
krishna

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)
  • Le modèle de mémoire ne garantit pas que vous verrez les dernières mises à jour d'un thread dans un autre thread, sans barrières de mémoire supplémentaires
  • Le fait d'incrémenter une variable n'est pas atomique de toute façon

Utilisez AtomicInteger etc pour les opérations thread-safe.

60
Jon Skeet

Les types primitifs ne sont pas thread-safe. Vérifiez ce tutoriel.

8
Parvin Gasimzade

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.

4
Jeff Miller
  1. 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.

  2. 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);
    }
}
0
Jacky_Cai