最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java中的Random和ThreadLocalRandom詳細(xì)解析

 更新時間:2024年01月27日 10:29:12   作者:魅Lemon  
這篇文章主要介紹了Java中的Random和ThreadLocalRandom詳細(xì)解析,Random 類用于生成偽隨機(jī)數(shù)的流, 該類使用48位種子,其使用線性同余公式進(jìn)行修改,需要的朋友可以參考下

一、Random類

1、簡介

Random 類用于生成偽隨機(jī)數(shù)的流。 該類使用48位種子,其使用線性同余公式進(jìn)行修改

  • Math.random()使用起來相對更簡單,但不是線程安全的;
  • java.util.Random的Random類是線程安全的。 但是跨線程的同時使用java.util.Random實例可能會遇到爭用,從而導(dǎo)致性能下降。 在多線程設(shè)計中考慮使用ThreadLocalRandom類;
  • java.util.Random的Random不是加密安全的。 考慮使用SecureRandom獲取一個加密安全的偽隨機(jī)數(shù)生成器,供安全敏感應(yīng)用程序使用

2、Random的構(gòu)造函數(shù)

Random():創(chuàng)建一個新的隨機(jī)數(shù)生成器

/**
 * Creates a new random number generator. This constructor sets
 * the seed of the random number generator to a value very likely
 * to be distinct from any other invocation of this constructor.
 */
public Random() {
    //System.nanoTime()返回正在運行的Java虛擬機(jī)的高分辨率時間源的當(dāng)前值,以納秒為單位。
    //這里會調(diào)用有參構(gòu)造函數(shù)
    this(seedUniquifier() ^ System.nanoTime());
}
private static long seedUniquifier() {
    // 線性同余生成元表  
    for (;;) {
        long current = seedUniquifier.get();
        long next = current * 181783497276652981L; 
        // 兩個很大的數(shù)相乘
        if (seedUniquifier.compareAndSet(current, next))
            // 這個比較并且交換CAS
            // 比較當(dāng)前工作內(nèi)存中的值和主內(nèi)存中的值,如果這個值是期望的,那么則執(zhí)行操作!
            //如果不是就一直循環(huán)!就是為了保證即使在多線程的環(huán)境中返回的也是不同的數(shù)
            return next;
    }
}
// atomic 這個是 juc 里面修飾的原子性的 long ,get方法說就是獲得這個構(gòu)造函數(shù)里面的值
 private static final AtomicLong seedUniquifier
        = new AtomicLong(8682522807148012L);

random(long seed):使用單個 Long 種子創(chuàng)建一個新的隨機(jī)數(shù)生成器

偽隨機(jī)使用了線性同余法(具體可自行查閱資料)

public Random(long seed) {
    if (getClass() == Random.class)
        this.seed = new AtomicLong(initialScramble(seed));
    else {
        // 子類可能重寫了這個不考慮
        this.seed = new AtomicLong(); // 創(chuàng)建一個新的AtomicLong,初始值為 0 
        setSeed(seed);
    }
}
    //清除nextGaussian()使用的haveNextNextGaussian 標(biāo)志,
    synchronized public void setSeed(long seed) {
        this.seed.set(initialScramble(seed));
        haveNextNextGaussian = false; 
     }
    private static long initialScramble(long seed) {
        return (seed ^ multiplier) & mask;
     }
  private static final long multiplier = 0x5DEECE66DL;
  // x & [(1L << 48)–1]與 x(mod 2^48)等價 取低位48位
  // 帶符號左移
  private static final long mask = (1L << 48) - 1;

3、next()核心方法

Random在多線程的環(huán)境是并發(fā)安全的,它解決競爭的方式是使用用原子類,本質(zhì)上上也就是CAS + Volatile保證線程安全

在這里插入圖片描述

在Random類中,有一個AtomicLong的域,用來保存隨機(jī)種子。其中每次生成隨機(jī)數(shù)時都會根據(jù)隨機(jī)種子做移位操作以得到隨機(jī)數(shù)。

//Long類型的隨機(jī)
//long類型在Java中總弄64bit,對next方法的返回值左移32作為long的高位,然后將next方法返回值作為低32位,作為long類型的隨機(jī)數(shù)。
//此處關(guān)鍵之處在于next方法
public long nextLong() {
    return ((long)(next(32)) << 32) + next(32);
}

以下是next方法的核心,使用seed種子,不斷生成新的種子,然后使用CAS將其更新,再返回種子的移位后值。這里不斷的循環(huán)CAS操作種子,直到成功??梢?,Random實現(xiàn)原理主要是利用隨機(jī)種子采用一定算法進(jìn)行處理生成隨機(jī)數(shù),在隨機(jī)種子的安全保證利用原子類AtomicLong。

protected int next(int bits) {
        long oldseed, nextseed;
        AtomicLong seed = this.seed;
        do {
            oldseed = seed.get();
            nextseed = (oldseed * multiplier + addend) & mask;
        } while (!seed.compareAndSet(oldseed, nextseed));
        return (int)(nextseed >>> (48 - bits));
    }

4、Random在并發(fā)下的缺點

雖然Random是線程安全,但是對于并發(fā)處理使用原子類AtomicLong在大量競爭時,使用同一個 Random 對象可能會導(dǎo)致線程阻塞,由于很多CAS操作會造成失敗,不斷的Spin,而造成CPU開銷比較大而且吞吐量也會下降。

這里可以自行多線程測試,可以發(fā)現(xiàn)隨著線程增加,Random隨著競爭越來越激烈,然后耗時越來越多。然而ThreadLocalRandom隨著線程數(shù)的增加,基本沒有變化。所以在大并發(fā)的情況下,隨機(jī)的選擇,可以考慮ThreadLocalRandom提升性能。

二、ThreadLocalRandom

1、簡介

ThreadLocalRandom是Random的子類,它是將Seed隨機(jī)種子隔離到當(dāng)前線程的隨機(jī)數(shù)生成器,從而解決了Random在Seed上競爭的問題,它的處理思想和ThreadLocal本質(zhì)相同。

Unsafe 類內(nèi)的方法透露著一股 “Unsafe” 的氣息,具體表現(xiàn)就是可以直接操作內(nèi)存,而不做任何安全校驗,如果有問題,則會在運行時拋出 Fatal Error,導(dǎo)致整個虛擬機(jī)的退出。

2、原理分析

2.1 ThreadLocalRandom單例模式

從下述代碼可以發(fā)現(xiàn)ThreadLocalRandom使用了單例模式,即在一個Java應(yīng)用中只有一個ThreadLocalRandom對象。

當(dāng)UNSAFE.getInt(Thread.currentThread(), PROBE)返回0時,就執(zhí)行l(wèi)ocalInit(),最后返回單例。

// 一個java應(yīng)用只有一個實例
// ThreadLocalRandom對象通過ThreadLocalRandom.current()獲取,之后可以直接返回隨機(jī)數(shù)
static final ThreadLocalRandom instance = new ThreadLocalRandom();
//獲取ThreadLocalRandom對象實例
public static ThreadLocalRandom current() {
    if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0)
        localInit();
    return instance;
}

2.2 Seed隨機(jī)種子隔離到當(dāng)前線程

核心方法

//會從 object 對象var1的內(nèi)存地址偏移var2后的位置讀取四個字節(jié)作為long型返回
public native long getLong(Object var1, long var2);
//可以將object對象var1的內(nèi)存地址偏移var2后的位置后四個字節(jié)設(shè)置為 var4
public native void putLong(Object var1, long var2, long var4);

UNSAFE.getInt(Thread.currentThread(), PROBE)是獲取當(dāng)前Thread線程對象中的PROBE。

首先獲取變量名SEED、PROBE等參數(shù)相對對象的偏移位置

// Unsafe mechanics
private static final sun.misc.Unsafe UNSAFE;
private static final long SEED;
private static final long PROBE;
private static final long SECONDARY;
static {
    try {
        UNSAFE = sun.misc.Unsafe.getUnsafe();
        Class<?> tk = Thread.class;
        SEED = UNSAFE.objectFieldOffset
            (tk.getDeclaredField("threadLocalRandomSeed"));
        PROBE = UNSAFE.objectFieldOffset
            (tk.getDeclaredField("threadLocalRandomProbe"));
        SECONDARY = UNSAFE.objectFieldOffset
            (tk.getDeclaredField("threadLocalRandomSecondarySeed"));
    } catch (Exception e) {
        throw new Error(e);
    }
}

當(dāng)?shù)谝淮握{(diào)用ThreadLocalRandom.current()方法時當(dāng)前線程檢測到PROBE未初始化會調(diào)用localInit()方法進(jìn)行初始化,并把當(dāng)前線程的seed值和probe值存儲在當(dāng)前線程Thread對象內(nèi)存地址偏移相對應(yīng)變量的位置

static final void localInit() {
    int p = probeGenerator.addAndGet(PROBE_INCREMENT);
    int probe = (p == 0) ? 1 : p; // skip 0
    long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT));
    Thread t = Thread.currentThread();
    UNSAFE.putLong(t, SEED, seed);
    UNSAFE.putInt(t, PROBE, probe);
}

threadLocalRandomProbe用于表示當(dāng)前線程Thread是否初始化,如果是非0,表示其已經(jīng)初始化。

換句話說,該變量就是狀態(tài)變量,用于標(biāo)識當(dāng)前線程Thread是否被初始化。

threadLocalRandomSeed從注釋中也可以看出,它是當(dāng)前線程的隨機(jī)種子。

隨機(jī)種子分散在各個Thread對象中,從而避免了并發(fā)時的競爭點。

3、nextSeed()核心方法

nextSeed()生成隨機(jī)種子用來生成隨機(jī)數(shù)序列

public long nextLong() {
    return mix64(nextSeed());
}

因為在初始化的時候已經(jīng)存儲了當(dāng)前線程的seed值和probe值到相應(yīng)線程對象內(nèi)存地址的偏移位置,調(diào)用nextSeed()時直接從當(dāng)前線程對象偏移位置處進(jìn)行獲取,并生成下一個隨機(jī)數(shù)種子到該位置,同時使用了UNSAFE類方法,不同線程間不需要競爭獲得seed值,因此可以可以將競爭點隔離

final long nextSeed() {
    Thread t; long r; // read and update per-thread seed
    UNSAFE.putLong(t = Thread.currentThread(), SEED,
                   r = UNSAFE.getLong(t, SEED) + GAMMA);
    return r;
}

三、總結(jié)

Random是Java中提供的隨機(jī)數(shù)生成器工具類,但是在大并發(fā)的情況下由于其隨機(jī)種子的競爭會導(dǎo)致吞吐量下降,從而引入ThreadLocalRandom它將競爭點隔離到每個線程中,從而消除了大并發(fā)情況下競爭問題,提升了性能。

并發(fā)競爭的整體優(yōu)化思路:lock -> cas + volatile -> free lock

到此這篇關(guān)于Java中的Random和ThreadLocalRandom詳細(xì)解析的文章就介紹到這了,更多相關(guān)Random和ThreadLocalRandom解析內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java random.nextInt的坑及解決

    java random.nextInt的坑及解決

    這篇文章主要介紹了java random.nextInt的坑及解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • SpringBoot整合logback的示例代碼

    SpringBoot整合logback的示例代碼

    Logback是由log4j創(chuàng)始人設(shè)計的又一個開源日志組件,logback分為三個模塊,在文章開頭給大家介紹的很明確,接下來通過本文重點介紹下SpringBoot整合logback的方法,需要的朋友可以參考下
    2022-04-04
  • Java實現(xiàn)帶附件的郵件發(fā)送功能

    Java實現(xiàn)帶附件的郵件發(fā)送功能

    這篇文章主要為大家詳細(xì)介紹了Java實現(xiàn)帶附件的郵件發(fā)送功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-08-08
  • Spring中使用Hystrix實現(xiàn)熔斷詳解

    Spring中使用Hystrix實現(xiàn)熔斷詳解

    這篇文章主要介紹了Java中使用Hystrix實現(xiàn)熔斷詳解,對于第一個問題,查看hystrix源碼可以看到,如果有緩存配置是優(yōu)先使用的緩存的,因此如果配置更新,必須要更新緩存,不能使用Hystrix.reset()方法來更新緩存,需要的朋友可以參考下
    2023-12-12
  • 使用jvm sandbox對三層嵌套類型的改造示例

    使用jvm sandbox對三層嵌套類型的改造示例

    這篇文章主要為大家介紹了使用jvm sandbox對三層嵌套類型的改造示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08
  • Spring學(xué)習(xí)筆記2之表單數(shù)據(jù)驗證、文件上傳實例代碼

    Spring學(xué)習(xí)筆記2之表單數(shù)據(jù)驗證、文件上傳實例代碼

    這篇文章主要介紹了Spring學(xué)習(xí)筆記2之表單數(shù)據(jù)驗證、文件上傳 的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2016-07-07
  • Springboot Websocket Stomp 消息訂閱推送

    Springboot Websocket Stomp 消息訂閱推送

    本文主要介紹了Springboot Websocket Stomp 消息訂閱推送,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-07-07
  • SpringBoot基于線程池的訂單創(chuàng)建并行化實踐過程

    SpringBoot基于線程池的訂單創(chuàng)建并行化實踐過程

    文章介紹了電商系統(tǒng)訂單創(chuàng)建接口的并行處理方案,通過分析業(yè)務(wù)流程、選擇合適的線程池和技術(shù),解決了高并發(fā)場景下的性能問題
    2025-12-12
  • jenkins如何部署應(yīng)用到多個環(huán)境

    jenkins如何部署應(yīng)用到多個環(huán)境

    本文介紹了如何基于流水線的方式將應(yīng)用程序部署到多個環(huán)境,包括測試環(huán)境和生產(chǎn)環(huán)境,通過創(chuàng)建項目、設(shè)置參數(shù)、配置流水線、設(shè)置環(huán)境變量、配置Maven工具、構(gòu)建階段、部署測試環(huán)境和生產(chǎn)環(huán)境、以及清理階段,實現(xiàn)了自動化部署流程
    2024-11-11
  • Java 線程池的作用以及該如何使用

    Java 線程池的作用以及該如何使用

    這篇文章主要介紹了Java 線程池的作用以及該如何使用,幫助大家更好的理解和學(xué)習(xí)Java的相關(guān)知識,感興趣的朋友可以了解下
    2021-01-01

最新評論

奉新县| 湘潭市| 太保市| 秦皇岛市| 内江市| 湾仔区| 通化县| 息烽县| 收藏| 长阳| 革吉县| 凤城市| 东宁县| 常州市| 土默特左旗| 汉阴县| 大冶市| 中方县| 武义县| 宜良县| 林口县| 阿巴嘎旗| 古交市| 宾阳县| 饶阳县| 新龙县| 如东县| 铜梁县| 武邑县| 铜鼓县| 桃园县| 南江县| 前郭尔| 满洲里市| 民乐县| 阳朔县| 建水县| 登封市| 横山县| 景谷| 宿松县|