java如何根據(jù)時(shí)間戳生成有序ID
引言
我們常用的主鍵有這么幾種
1. 數(shù)據(jù)庫(kù)自增主鍵,比如mysql的autoincrement,這種插入快,但是識(shí)別度不高
2. uuid 這個(gè)號(hào)稱是全球唯一的,但是無(wú)序,沒(méi)有實(shí)際意義,只能保證唯一
3. 時(shí)間戳,這種在分布式的場(chǎng)景下就需要考慮更多種情況
4. 雪花算法 snow flake ,分布式全局唯一主鍵,很牛,但是我覺(jué)得用起來(lái)也挺麻煩哈哈哈
所以在并發(fā)情況沒(méi)那么大的時(shí)候用一個(gè)工具類(lèi)搞定,我就是這么懶
工具類(lèi)
@Slf4j
public class NumUtil {
private static long tmpID = 0;
private static final long LOCK_TIME = 1;
private static final long INCREASE_STEP = 1;
private static SimpleDateFormat sdf = new SimpleDateFormat("yyMMddHHmmssSSS");
private static final Lock LOCK = new ReentrantLock();
public static long nextPkId() throws InterruptedException {
//當(dāng)前:(年、月、日、時(shí)、分、秒、毫秒)
long timeCount;
if (LOCK.tryLock(LOCK_TIME, TimeUnit.SECONDS)) {
timeCount = Long.parseLong(sdf.format(new Date()));
try {
if (tmpID < timeCount) {
tmpID = timeCount;
} else {
tmpID += INCREASE_STEP;
timeCount = tmpID;
}
return timeCount;
} finally {
LOCK.unlock();
}
} else {
log.error("lock failed");
return nextPkId();
}
}
}
貼上代碼,這里用了當(dāng)前時(shí)間,精確到毫秒級(jí),如果有需要的話可以在實(shí)例化timeCount的時(shí)候乘以10或者100 1000之類(lèi)的,這個(gè)看大家,然后加上鎖,防止線程不安全的情況,加鎖失敗的時(shí)候遞歸,再來(lái)一次。
也可以使用synchronized做成同步方法,當(dāng)中的區(qū)別下次再討論。
有評(píng)論說(shuō)宕機(jī)會(huì)導(dǎo)致tmpID歸0導(dǎo)致已經(jīng)使用過(guò)超出當(dāng)前時(shí)間的ID,所以持久化這個(gè)tmpID也是可以的。
但這也就是在并發(fā)沒(méi)那么高的情況下才使用這種方法,一般并發(fā)場(chǎng)景下還是分布式鎖+推特的雪花算法解決。
測(cè)試
public static void numTest() {
ExecutorService executorService = Executors.newCachedThreadPool();
int n = 10000;
List<Long> list = new ArrayList<>();
CountDownLatch latch = new CountDownLatch(n);
for (int i = 0; i < n; i++) {
executorService.execute(() -> {
//執(zhí)行業(yè)務(wù)請(qǐng)求
try {
list.add(NumUtil.nextPkId());
} catch (InterruptedException e) {
e.printStackTrace();
}
latch.countDown();
});
}
try {
// 一定記得加上timeout時(shí)間,防止阻塞主線程
latch.await(3000, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
log.error(e.getMessage());
}
//4.等待所有子任務(wù)完成,組裝內(nèi)容
while (list.size() < n) {
log.info("集合長(zhǎng)度 >>> {}",list.size());
}
//5.關(guān)閉線程池
executorService.shutdown();
for (Long aLong : list) {
System.out.println(aLong);
}
}
然后噼里啪啦打印了一萬(wàn)個(gè)ID,沒(méi)有重復(fù)的,一秒以內(nèi)生成

結(jié)論
當(dāng)然這種只是為了單體或者是并發(fā)沒(méi)有高到那么離譜的場(chǎng)景下使用,效率我覺(jué)得還不錯(cuò),分布式的場(chǎng)景下可能需要用到redis的自增來(lái)計(jì)數(shù)以達(dá)到數(shù)據(jù)安全的效果
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家
相關(guān)文章
Spring Security獲取用戶認(rèn)證信息的實(shí)現(xiàn)流程
Spring Security是一個(gè)能夠?yàn)榛赟pring的企業(yè)應(yīng)用系統(tǒng)提供聲明式的安全訪問(wèn)控制解決方案的安全框架。它提供了一組可以在Spring應(yīng)用上下文中配置的Bean,充分利用了Spring IoC,DI和AOP功能,為應(yīng)用系統(tǒng)提供聲明式的安全訪問(wèn)控制功能2022-12-12
win10 eclipse配置環(huán)境變量的教程圖解
本文通過(guò)圖文并茂的形式給大家介紹了win10 eclipse配置環(huán)境變量的方法,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2018-07-07
springboot @validated List校驗(yàn)失效問(wèn)題
這篇文章主要介紹了springboot @validated List校驗(yàn)失效問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07
springboot動(dòng)態(tài)加載jar包動(dòng)態(tài)配置實(shí)例詳解
這篇文章主要給大家介紹了關(guān)于springboot動(dòng)態(tài)加載jar包動(dòng)態(tài)配置的相關(guān)資料,在項(xiàng)目開(kāi)發(fā)的過(guò)程中,有時(shí)候需要?jiǎng)討B(tài)靈活的加載某個(gè)jar包并執(zhí)行其里面的方法的時(shí)候,需要的朋友可以參考下2023-11-11
SpringBoot實(shí)現(xiàn)國(guó)際化的配置方法
隨著一個(gè)系統(tǒng)的規(guī)模做上去之后,國(guó)際化的問(wèn)題就會(huì)逐漸暴露出來(lái),簡(jiǎn)單來(lái)說(shuō),當(dāng)你的系統(tǒng)面對(duì)的不再是本國(guó)的用戶,而要面臨海外用戶時(shí),系統(tǒng)必須要能適配國(guó)際化,所以本文給大家介紹了SpringBoot實(shí)現(xiàn)國(guó)際化的配置方法,需要的朋友可以參考下2025-11-11
Java利用OSHI實(shí)現(xiàn)獲取機(jī)器的硬件信息
OSHI(Operating System and Hardware Information)是一個(gè)開(kāi)源的Java庫(kù),用于獲取操作系統(tǒng)和硬件的詳細(xì)信息,下面我們就來(lái)看看他的具體使用吧2024-11-11
SpringCloud組件之Eureka Server詳細(xì)啟動(dòng)過(guò)程及說(shuō)明
這篇文章主要介紹了SpringCloud組件之Eureka Server詳細(xì)啟動(dòng)過(guò)程及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01
IDEA項(xiàng)目如何取消git版本管控并添加svn版本控制
在公司內(nèi)部服務(wù)器環(huán)境下,將代碼倉(cāng)庫(kù)從Gitee的Git遷移到SVN可以避免外部版本控制的風(fēng)險(xiǎn),遷移過(guò)程中,先刪除項(xiàng)目的.git文件夾,再通過(guò)Eclipse的設(shè)置界面刪除原Git配置并添加SVN配置,之后,將項(xiàng)目提交到SVN倉(cāng)庫(kù),確保使用ignore列表過(guò)濾不必要的文件2024-10-10

