1 前提
因为看了AtomicInteger 发现他的锁的实现基于CAS,那同样的道理,我们也可以设计一个秒杀系统
1.1 当秒杀来临时 定义线程池去处理
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 模拟高并发 一秒1百万并发
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);
}
});
});
}
关键代码 1.3
// Unsafe mechanics java 留给
private static final sun.misc.Unsafe U;
private static final long INIT_CONTROL;
//静态代码块
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以下是业务代码
public void testCasMulti(User user) {
BigDecimal One = new BigDecimal(1);
while (true) {
//先检查库存有木有 如果没有就进行下面的操作
Integer param = (Integer) redisUtil.get("kucun");
if(param<=0){
//log.info("停止循环");
break;
}
int initControlLocal = initControl;
/**
* 如果已经有线程在进行获取了,则直接放弃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;
}
/**
* 争夺控制权
*/
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;
看下运行结果
1.5运行过程
1)当大量请求过来时,首先由线程池执行任务,线程池不断的把任务分配给线程,corepoolsize不足,就把任务缓存到队列,队列已经满了,就继续开线程,直接到最大值max时,就丢弃任务。
2)线程池执行的任务调用业务层,发现有一些线程先抢占了任务,其他线程睡眠,或者自旋等待
3)获取锁的线程执行扣减库存的任务
【源码地址】https://github.com/pw09066310/my.git