JAVA使用Lock實現多線程并發生成唯一的流水號
今天在工作java開發過程中遇見需要生成十位數流水號的工作場景,本文將講述下利用ReentrantLock實現多線程并發生成唯一的流水號的功能,有些情況可以采用數據庫自定義序列號自增生成流水號,亦或是自己編寫數據庫觸發器生成流水號。
但本文以代碼為主,記錄在代碼層面上如何利用ReentrantLock,實現多線程同時請求時也能確保流水號取得唯一的場景。
ReentrantLock鎖原理在本文中不再闡述,詳細的講解可參考此博主的講解:
https://blog.csdn.net/zhengzhaoyang122/article/details/110847701
1.編寫工具類生成流水號
ReentrantLock鎖的運用其實很簡單,在方法中如下添加即可,簡單的調用業務前先上鎖,調用完后釋放鎖。這里我采用簡單的渠道號(四位)+年月日(8位)+流水號(10位)的規則生成業務使用的流水號。
public class SerialNumUtil {
/**記錄初始值*/
public static int num = 0;
private static ReentrantLock lock = new ReentrantLock();
public static String getNum(String channelId) {
String unique = "";
//上鎖
lock.lock();
try {
//------------------流水號業務邏輯----------------
//5位流水號
if (num == 0) {
//當天第一份流水單號
unique = channelId + new SimpleDateFormat("yyyyMMdd").format(new Date()) + "0000000001";
} else {
//當天最后的訂單流水號累加1
String nums = String.valueOf(num + 1);
//設定具體流水為十位數,單數則補齊前面的0
StringBuilder sb = new StringBuilder(nums);
for (int i = nums.length(); i < 10; i++) {
sb.insert(0, "0");
}
unique = channelId + new SimpleDateFormat("yyyyMMdd").format(new Date()) + sb.toString();
}
//已有流水單+1
num++;
//----------------------------------
} catch (Exception e) {
e.printStackTrace();
} finally {
//釋放鎖
lock.unlock();
}
return unique;
}
}
2.編寫測試類調用驗證
這里我們利用CyclicBarrier類來模仿進行大量并發的測試。CyclicBarrier是JDK1.5的java.util.concurrent并發包中提供的一個并發工具類.
a.準備一個Task線程類
public class Task implements Runnable {
private static final Logger logger = Logger.getLogger("log");
private CyclicBarrier cyclicBarrier;
public Task(CyclicBarrier cyclicBarrier) {
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
try {
// 等待所有任務準備就緒
cyclicBarrier.await();
// 測試內容
logger.info(SerialNumUtil.getNum("2050"));
} catch (Exception e) {
e.printStackTrace();
}
}
}
b.主方法作為測試入口設定同時并發100個線程進行打印測試驗證。
public class TestLock {
public static void main(String[] args) {
int count = 100;
CyclicBarrier cyclicBarrier = new CyclicBarrier(count);
ExecutorService executorService = Executors.newFixedThreadPool(count);
for (int i = 0; i < count; i++) {
executorService.execute(new Task(cyclicBarrier));
}
executorService.shutdown();
while (!executorService.isTerminated()) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

可以看到同一秒里,我們每條線程獲取到的流水號都是唯一不重復的,所以完美實現了此功能!
本功能也是在網絡上查找很多文章才實現的,感謝各位大佬!


浙公網安備 33010602011771號