Generer des Nombres Aleatoires en Java : Guide Complet avec Random, ThreadLocalRandom et SecureRandom

Apprenez a generer des nombres aleatoires en Java avec les classes Random, ThreadLocalRandom et SecureRandom. Guide complet avec exemples de code, bonnes pratiques et pieges a eviter.

Mahmoud DEVO
Mahmoud DEVO
December 27, 2025 7 min read
Generer des Nombres Aleatoires en Java : Guide Complet avec Random, ThreadLocalRandom et SecureRandom

Generer des Nombres Aleatoires en Java : Guide Complet

Introduction

La generation de nombres aleatoires est une fonctionnalite fondamentale dans de nombreuses applications Java. Que ce soit pour creer des jeux video, effectuer des simulations statistiques, generer des identifiants uniques, ou implementer des mecanismes de securite cryptographique, comprendre comment generer des nombres aleatoires de maniere appropriee est essentiel pour tout developpeur Java.

Java offre plusieurs approches pour generer des nombres aleatoires, chacune adaptee a des cas d’utilisation specifiques. Dans cet article, nous explorerons en profondeur les trois principales classes de generation de nombres aleatoires : Random, ThreadLocalRandom et SecureRandom. Nous verrons egalement comment choisir la bonne classe selon vos besoins et comment eviter les erreurs courantes.

Les Differentes Classes de Generation

La Classe Random : L’Approche Classique

La classe java.util.Random est la solution la plus ancienne et la plus simple pour generer des nombres pseudo-aleatoires en Java. Elle utilise un algorithme de congruence lineaire qui produit une sequence de nombres qui semble aleatoire.

import java.util.Random;

public class RandomBasicExample {
    public static void main(String[] args) {
        // Creation d'une instance Random
        Random random = new Random();

        // Differents types de nombres aleatoires
        int randInt = random.nextInt();           // Entier quelconque
        int randIntBound = random.nextInt(100);   // Entier entre 0 et 99
        long randLong = random.nextLong();        // Long quelconque
        double randDouble = random.nextDouble();  // Double entre 0.0 et 1.0
        float randFloat = random.nextFloat();     // Float entre 0.0 et 1.0
        boolean randBool = random.nextBoolean();  // true ou false

        System.out.println("Entier aleatoire: " + randInt);
        System.out.println("Entier borne [0-99]: " + randIntBound);
        System.out.println("Long aleatoire: " + randLong);
        System.out.println("Double aleatoire: " + randDouble);
        System.out.println("Float aleatoire: " + randFloat);
        System.out.println("Boolean aleatoire: " + randBool);
    }
}

Utilisation d’une Seed pour la Reproductibilite

Une caracteristique importante de Random est la possibilite d’utiliser une “seed” (graine) pour reproduire exactement la meme sequence de nombres :

import java.util.Random;

public class RandomSeedExample {
    public static void main(String[] args) {
        // Meme seed = meme sequence
        Random random1 = new Random(12345);
        Random random2 = new Random(12345);

        System.out.println("Sequence 1:");
        for (int i = 0; i < 5; i++) {
            System.out.print(random1.nextInt(100) + " ");
        }

        System.out.println("\nSequence 2 (identique):");
        for (int i = 0; i < 5; i++) {
            System.out.print(random2.nextInt(100) + " ");
        }
        // Les deux sequences seront identiques !
    }
}

La Classe ThreadLocalRandom : Performance en Multithreading

Introduite dans Java 7, ThreadLocalRandom est optimisee pour les environnements multithread. Contrairement a Random, elle ne souffre pas de contention entre threads.

import java.util.concurrent.ThreadLocalRandom;

public class ThreadLocalRandomExample {
    public static void main(String[] args) {
        // Pas de new, on utilise current()
        ThreadLocalRandom tlr = ThreadLocalRandom.current();

        // Generation de nombres dans une plage
        int randInt = tlr.nextInt(1, 101);        // Entre 1 et 100 inclus
        long randLong = tlr.nextLong(1000, 10000); // Entre 1000 et 9999
        double randDouble = tlr.nextDouble(0.0, 1.0);

        System.out.println("Entier [1-100]: " + randInt);
        System.out.println("Long [1000-9999]: " + randLong);
        System.out.println("Double [0.0-1.0]: " + randDouble);
    }
}

Exemple d’Utilisation en Contexte Multithread

import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ThreadLocalRandomMultithreadExample {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(4);

        for (int i = 0; i < 10; i++) {
            executor.submit(() -> {
                // Chaque thread utilise sa propre instance
                int random = ThreadLocalRandom.current().nextInt(1000);
                String threadName = Thread.currentThread().getName();
                System.out.println(threadName + " -> " + random);
            });
        }

        executor.shutdown();
        executor.awaitTermination(5, TimeUnit.SECONDS);
    }
}

La Classe SecureRandom : Securite Cryptographique

Pour les applications necessitant une securite forte (generation de cles, tokens, mots de passe), SecureRandom est la seule option acceptable.

import java.security.SecureRandom;
import java.util.Base64;

public class SecureRandomExample {
    public static void main(String[] args) {
        SecureRandom secureRandom = new SecureRandom();

        // Generation de bytes aleatoires (pour cles, tokens, etc.)
        byte[] randomBytes = new byte[32];
        secureRandom.nextBytes(randomBytes);

        // Conversion en Base64 pour affichage
        String token = Base64.getEncoder().encodeToString(randomBytes);
        System.out.println("Token securise: " + token);

        // Generation d'un entier securise
        int secureInt = secureRandom.nextInt(1000000);
        System.out.println("Code OTP: " + String.format("%06d", secureInt % 1000000));
    }
}

Generation de Mots de Passe Securises

import java.security.SecureRandom;

public class SecurePasswordGenerator {
    private static final String CHARS =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*";

    public static String generatePassword(int length) {
        SecureRandom random = new SecureRandom();
        StringBuilder password = new StringBuilder(length);

        for (int i = 0; i < length; i++) {
            int index = random.nextInt(CHARS.length());
            password.append(CHARS.charAt(index));
        }

        return password.toString();
    }

    public static void main(String[] args) {
        System.out.println("Mot de passe genere: " + generatePassword(16));
    }
}

Generation dans une Plage Specifique

Plage avec Borne Inferieure et Superieure

import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;

public class RangeExample {
    // Methode classique avec Random
    public static int randomInRange(Random random, int min, int max) {
        return random.nextInt(max - min + 1) + min;
    }

    public static void main(String[] args) {
        Random random = new Random();

        // Generer un nombre entre 50 et 100 (inclus)
        int result = randomInRange(random, 50, 100);
        System.out.println("Random classique [50-100]: " + result);

        // Avec ThreadLocalRandom (plus simple)
        int tlrResult = ThreadLocalRandom.current().nextInt(50, 101);
        System.out.println("ThreadLocalRandom [50-100]: " + tlrResult);
    }
}

Generation de Nombres Sans Doublons

Voici une implementation complete et optimisee pour generer des nombres aleatoires sans doublons :

import java.util.*;
import java.util.concurrent.ThreadLocalRandom;

public class UniqueRandomNumbers {

    // Methode avec Collections.shuffle (recommandee)
    public static List<Integer> getUniqueRandomsShuffle(int min, int max, int count) {
        if (count > (max - min + 1)) {
            throw new IllegalArgumentException("Count exceeds available range");
        }

        List<Integer> numbers = new ArrayList<>();
        for (int i = min; i <= max; i++) {
            numbers.add(i);
        }
        Collections.shuffle(numbers);
        return numbers.subList(0, count);
    }

    // Methode avec Set (pour grandes plages)
    public static Set<Integer> getUniqueRandomsSet(int min, int max, int count) {
        Set<Integer> uniqueNumbers = new LinkedHashSet<>();
        ThreadLocalRandom random = ThreadLocalRandom.current();

        while (uniqueNumbers.size() < count) {
            uniqueNumbers.add(random.nextInt(min, max + 1));
        }

        return uniqueNumbers;
    }

    public static void main(String[] args) {
        // 6 nombres uniques entre 1 et 49 (comme au Loto)
        List<Integer> lotoNumbers = getUniqueRandomsShuffle(1, 49, 6);
        Collections.sort(lotoNumbers);
        System.out.println("Numeros Loto: " + lotoNumbers);

        // 10 nombres uniques entre 1 et 1000
        Set<Integer> uniqueSet = getUniqueRandomsSet(1, 1000, 10);
        System.out.println("10 nombres uniques: " + uniqueSet);
    }
}

Utilisation avec les Streams Java 8+

Java 8 a introduit des methodes de generation de streams aleatoires :

import java.util.Random;
import java.util.stream.IntStream;
import java.util.concurrent.ThreadLocalRandom;

public class RandomStreamsExample {
    public static void main(String[] args) {
        Random random = new Random();

        // Stream infini de nombres aleatoires
        System.out.println("5 entiers aleatoires:");
        random.ints()
              .limit(5)
              .forEach(System.out::println);

        // Stream borne
        System.out.println("\n5 entiers entre 1 et 100:");
        random.ints(5, 1, 101)
              .forEach(System.out::println);

        // Avec ThreadLocalRandom
        System.out.println("\nSomme de 1000 nombres aleatoires:");
        long sum = ThreadLocalRandom.current()
                                    .ints(1000, 0, 100)
                                    .sum();
        System.out.println("Somme: " + sum);

        // Statistiques sur les nombres generes
        System.out.println("\nStatistiques sur 10000 nombres:");
        IntStream.generate(() -> ThreadLocalRandom.current().nextInt(100))
                 .limit(10000)
                 .summaryStatistics();
    }
}

Bonnes Pratiques

1. Choisir la Bonne Classe

Cas d’utilisationClasse recommandee
Usage general, simpleRandom
Applications multithreadThreadLocalRandom
Securite, cryptographieSecureRandom
Tests reproductiblesRandom avec seed

2. Reutiliser les Instances

// MAUVAIS : Creer une nouvelle instance a chaque appel
public int badRandom() {
    return new Random().nextInt(100); // Ne pas faire ca !
}

// BON : Reutiliser une instance
private static final Random RANDOM = new Random();
public int goodRandom() {
    return RANDOM.nextInt(100);
}

// MEILLEUR en multithread : ThreadLocalRandom
public int bestRandom() {
    return ThreadLocalRandom.current().nextInt(100);
}

3. Ne Jamais Utiliser Math.random() pour la Securite

// DANGEREUX pour la securite !
double unsafeRandom = Math.random();

// SECURISE
SecureRandom secureRandom = new SecureRandom();
double safeRandom = secureRandom.nextDouble();

4. Initialiser SecureRandom Correctement

// Laisser le systeme choisir le meilleur algorithme
SecureRandom secure = new SecureRandom();

// Ou specifier explicitement (si necessaire)
SecureRandom secureSha1 = SecureRandom.getInstance("SHA1PRNG");

Pieges Courants a Eviter

1. Biais dans la Generation avec Modulo

// MAUVAIS : Biais subtil avec modulo
int biased = Math.abs(random.nextInt()) % 100;

// BON : Utiliser nextInt(bound)
int unbiased = random.nextInt(100);

2. Seed Previsible

// MAUVAIS : Seed base sur le temps (previsible)
Random predictable = new Random(System.currentTimeMillis());

// BON : Laisser Java choisir la seed
Random unpredictable = new Random();

// MEILLEUR pour la securite : SecureRandom
SecureRandom secure = new SecureRandom();

3. Partage de Random entre Threads

// MAUVAIS : Contention et problemes de performance
private static final Random SHARED = new Random();
// Utilise par plusieurs threads...

// BON : ThreadLocalRandom pour le multithreading
private int getRandomValue() {
    return ThreadLocalRandom.current().nextInt(100);
}

4. Oublier les Bornes Exclusives

// nextInt(100) retourne [0, 99], pas [0, 100] !
int zeroTo99 = random.nextInt(100);  // 100 est EXCLU

// Pour inclure 100 :
int zeroTo100 = random.nextInt(101); // Maintenant 100 est inclus

5. Utiliser Random pour des Applications Sensibles

// JAMAIS pour la generation de tokens, cles, ou mots de passe !
String unsafeToken = String.valueOf(new Random().nextLong());

// TOUJOURS utiliser SecureRandom
SecureRandom secure = new SecureRandom();
byte[] tokenBytes = new byte[32];
secure.nextBytes(tokenBytes);
String safeToken = Base64.getEncoder().encodeToString(tokenBytes);

Comparaison des Performances

import java.security.SecureRandom;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;

public class PerformanceComparison {
    private static final int ITERATIONS = 10_000_000;

    public static void main(String[] args) {
        // Random
        Random random = new Random();
        long start = System.nanoTime();
        for (int i = 0; i < ITERATIONS; i++) {
            random.nextInt(100);
        }
        long randomTime = System.nanoTime() - start;

        // ThreadLocalRandom
        start = System.nanoTime();
        for (int i = 0; i < ITERATIONS; i++) {
            ThreadLocalRandom.current().nextInt(100);
        }
        long tlrTime = System.nanoTime() - start;

        // SecureRandom
        SecureRandom secure = new SecureRandom();
        start = System.nanoTime();
        for (int i = 0; i < ITERATIONS; i++) {
            secure.nextInt(100);
        }
        long secureTime = System.nanoTime() - start;

        System.out.printf("Random:            %d ms%n", randomTime / 1_000_000);
        System.out.printf("ThreadLocalRandom: %d ms%n", tlrTime / 1_000_000);
        System.out.printf("SecureRandom:      %d ms%n", secureTime / 1_000_000);
    }
}

Conclusion

La generation de nombres aleatoires en Java offre plusieurs options adaptees a differents besoins :

  • Random : Simple et efficace pour les cas d’utilisation generaux sans contraintes de securite ou de multithreading intensif.
  • ThreadLocalRandom : Le choix ideal pour les applications multithread grace a ses performances superieures et son absence de contention.
  • SecureRandom : Indispensable pour toute application necessitant une securite cryptographique (tokens, mots de passe, cles).

Choisissez toujours la classe appropriee en fonction de vos besoins specifiques. N’utilisez jamais Random ou Math.random() pour des fonctionnalites liees a la securite, et privilegiez ThreadLocalRandom dans les environnements concurrents pour des performances optimales.

Ressources Complementaires

Advertisement

In-Article Ad

Dev Mode

Share this article

Mahmoud DEVO

Mahmoud DEVO

Senior Full-Stack Developer

I'm a passionate full-stack developer with 10+ years of experience building scalable web applications. I write about Vue.js, Node.js, PostgreSQL, and modern DevOps practices.

Enjoyed this article?

Subscribe to get more tech content delivered to your inbox.

Related Articles