1 前提
因?yàn)榭戳薃tomicInteger 發(fā)現(xiàn)他的鎖的實(shí)現(xiàn)基于CAS,那同樣的道理,我們也可以設(shè)計(jì)一個秒殺系統(tǒng)
1.1 當(dāng)秒殺來臨時 定義線程池去處理
ThreadPoolExecutor executor = new ThreadPoolExecutor(100, 100,
60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1000), new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
//定義線程池的拒絕策略 直接丟棄
log.info("discard:{}",r);
}
});
1.2 模擬高并發(fā) 一秒1百萬并發(fā)
public void testCas(){
CyclicBarrier barrier = new CyclicBarrier(100);
List<Integer> list=new ArrayList();
IntStream.range(0,1000000).forEach(list::add);
list.parallelStream().forEach(o->{
int finalI = o;
executor.submit(new Runnable() {
@Override
public void run() {
User user=new User();
user.setUserId(finalI);
casService.testCasMulti(user);
}
});
});
}
關(guān)鍵代碼 1.3
// Unsafe mechanics java 留給
private static final sun.misc.Unsafe U;
private static final long INIT_CONTROL;
//靜態(tài)代碼塊
static {
try {
// U = sun.misc.Unsafe.getUnsafe();
//初始化 通過反射
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
//獲取unsafe
U = (Unsafe) f.get(null);
Class<?> k = CasServiceImpl.class;
//將 initControl的值初始化給INIT_CONTROL 完成后面的compareAndSwapInt()操作
INIT_CONTROL = U.objectFieldOffset
(k.getDeclaredField("initControl"));
} catch (Exception e) {
throw new Error(e);
}
}
1.4以下是業(yè)務(wù)代碼
public void testCasMulti(User user) {
BigDecimal One = new BigDecimal(1);
while (true) {
//先檢查庫存有木有 如果沒有就進(jìn)行下面的操作
Integer param = (Integer) redisUtil.get("kucun");
if(param<=0){
//log.info("停止循環(huán)");
break;
}
int initControlLocal = initControl;
/**
* 如果已經(jīng)有線程在進(jìn)行獲取了,則直接放棄cpu
*/
if (initControlLocal < 0) {
// log.info("initControlLocal < 0,just yield and wait");
/**
* 這里可以不要這個,可以睡眠一小會。
*/
// Thread.yield();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
log.warn("e:{}", e);
}
continue;
}
/**
* 爭奪控制權(quán)
*/
boolean bGotChanceToInit = U.compareAndSwapInt(this,
INIT_CONTROL, initControlLocal, -1);
if (bGotChanceToInit) {
try {
log.info("用戶user={},獲取到競爭鎖",user.getUserId());
//扣減庫存
redisUtil.decr("kucun",1L);
} finally {
initControl = 0;
}
break;
}
}
}
private volatile int initControl;
看下運(yùn)行結(jié)果

image.png

image.png
1.5運(yùn)行過程
1)當(dāng)大量請求過來時,首先由線程池執(zhí)行任務(wù),線程池不斷的把任務(wù)分配給線程,corepoolsize不足,就把任務(wù)緩存到隊(duì)列,隊(duì)列已經(jīng)滿了,就繼續(xù)開線程,直接到最大值max時,就丟棄任務(wù)。
2)線程池執(zhí)行的任務(wù)調(diào)用業(yè)務(wù)層,發(fā)現(xiàn)有一些線程先搶占了任務(wù),其他線程睡眠,或者自旋等待
3)獲取鎖的線程執(zhí)行扣減庫存的任務(wù)
【源碼地址】https://github.com/pw09066310/my.git