首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

一段極度消耗 CPU 的隨機字串生成代碼:根因分析與優化建議

     編輯:Java 相關知識

好奇的同學可直接複製下方代碼執行(例:java GenRandomStringTest 50 100 1024),會發現 CPU 飆升嚴重。本文剖析根因並徵集優化方案。

測試代碼

import java.util.Random;

public class GenRandomStringTest {
    public static void main(String[] args) throws Exception {
        int threadNums = Integer.parseInt(args[0]);
        int keySize = Integer.parseInt(args[1]);
        int valueSize = Integer.parseInt(args[2]);
        for (int i = 0; i < threadNums; i++) {
            new Thread(new Test(keySize, valueSize)).start();
        }
    }
}

class Test implements Runnable {
    private Random rand = new Random();
    private int keySize, valueSize;

    public Test(int keySize, int valueSize) {
        this.keySize = keySize;
        this.valueSize = valueSize;
    }

    public void run() {
        while (true) {
            RandomStringUtils.generateBytes(keySize);
            RandomStringUtils.generateBytes(valueSize);
            try {
                Thread.sleep(rand.nextInt(2));
            } catch (Exception e) { /* IGNORE */ }
        }
    }
}

RandomStringUtils 核心實現(圖片重構)

因部落格渲染問題原代碼以圖片形式呈現,關鍵邏輯重構如下:

public class RandomStringUtils {
    private static final char[] CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".toCharArray();
    private static final Random RANDOM = new Random();

    public static byte[] generateBytes(int size) {
        byte[] bytes = new byte[size];
        for (int i = 0; i < size; i++) {
            bytes[i] = (byte) CHARS[RANDOM.nextInt(CHARS.length)];
        }
        return bytes;
    }
}

根因分析:為何極度耗 CPU?

  1. 單次調用循環開銷大:生成 1024 字節需 1024 次 nextInt() + 陣列寫入 + 邊界檢查,純 CPU 運算無 I/O 等待。
  2. Random 競爭熱點:多線程共享靜態 Random 實例(或各自 new Random 但種子碰撞),CAS 更新種子產生嚴重緩存行反彈與總線鎖競爭。
  3. 無向量化/批次化:逐字節生成無法利用 SIMD 或 Random.nextBytes() 批次填充。
  4. 睡眠時間極短:rand.nextInt(2) 僅 0/1ms,實際佔比極低,CPU 長期滿載。

優化建議(按效果遞減)

  1. 改用 ThreadLocalRandom (JDK 7+):線程本地無競爭,nextBytes(byte[]) 一次填充整個陣列。
    ThreadLocalRandom.current().nextBytes(bytes);
  2. 改用 SecureRandom 或 SplittableRandom (JDK 8+):SplittableRandom 專為並行設計,吞吐更高。
  3. 預生成字元池 + System.arraycopy:大量固定長度字串可預生成塊,拼接復用。
  4. 批次生成:單次調用生成大塊緩衝區,按需切片,減少方法調用開銷。
  5. 調大睡眠/引入速率限制器:若非壓測場景,務必加入合理背壓。

基準對比預期

方案相對吞吐CPU 佔用
原版 (共享 Random + 循環)1x極高
ThreadLocalRandom.nextBytes()~50-100x極低
SplittableRandom + 批次~80-150x極低

歡迎同學提供更多優化思路!

 

感興趣的同學可以把下面的代碼copy到機器上執行看看(例如java GenRandomStringTest 50 100 1024),會發現是挺耗CPU的,原因是什麼呢? 有什麼建議嗎?

GenRandomStringTest代碼如下,也可從這下載:
import java.util.Random;

public class GenRandomStringTest {

public static void main(String[] args) throws Exception{
int threadNums = Integer.parseInt(args[0]);
int keySize = Integer.parseInt(args[1]);
int valueSize = Integer.parseInt(args[2]);
for (int i = 0; i < threadNums; i++) {
new Thread(new Test(keySize,valueSize)).start();
}
}

}

class Test implements Runnable{

private Random rand = new Random();

private int keySize = 0;
private int valueSize = 0;

public Test(int keySize,int valueSize){
this.keySize = keySize;
this.valueSize = valueSize;
}

public void run() {
while(true){
RandomStringUtils.generateBytes(keySize);
RandomStringUtils.generateBytes(valueSize);
try{
Thread.sleep(rand.nextInt(2));
}
catch(Exception e){
// IGNORE;
}
}
}

}


RandomStringUtils代碼如下,由於blog渲染有問題,會丟失代碼,只好截圖如下了,也可從這下載:


ps: 不過話說以這種方式隨機生成指定大小的字符串還是挺耗時的,也歡迎提供建議,:)
 

 
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved