一:Disruptor簡單說一說
Disruptor 是一個 Java 的併發編程框架,大大的簡化了併發程序開發的難度,在性能上也比 Java 本身提供的一些併發包要好。
Disruptor原理:採用了一個數組,循環使用這個數組,從而形成了環。裏面有一個序號管理器,使消費者和生產者之前快速正確地傳輸數據。RingBuffer中每個數據的序號,用於跟蹤ringbuffer中任務的變化和消費者的消費情況,這個序號是一直遞增的。在併發的情況下沒有使用鎖,而是使用了cas檢驗,不用Disruptor也可以去好好看看這個思想和原理,挺不錯的。
github上面生產者消費者的demo:https://github.com/LMAX-Exchange/disruptor/wiki/Getting-Started
二:直接上代碼
但是首先你要導入Disruptor包哦:
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.2</version>
</dependency>
其中操作的類都寫到了靜態內部類,就不一個類一個類的貼代碼了直接一次貼完:
import com.lmax.disruptor.EventTranslatorVararg;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.WorkHandler;
import com.lmax.disruptor.dsl.Disruptor;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;
/**
* @author yh128
* @version 1.0.0
* @ClassName Mains.java
* @Description Disruptor案例
* @Param
* @createTime 2019年12月12日 10:58:00
*/
@Slf4j
public class Mains {
private static List<BuyEventWork.Order> lists = new ArrayList<>();
private static final Random random = new Random();
public static void main(String[] args) throws InterruptedException {
int bufferSize = 1024;
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(4, 6, 0, TimeUnit.SECONDS,
new LinkedBlockingDeque<>());
Disruptor<BuyEvent> disruptor = new Disruptor<>(BuyEvent::new, bufferSize,
r -> {
return new Thread(r);
});
disruptor.handleEventsWithWorkerPool(new BuyEventWork(), new BuyEventWork(), new BuyEventWork(), new BuyEventWork(),
new BuyEventWork(), new BuyEventWork(), new BuyEventWork(), new BuyEventWork(), new BuyEventWork(),
new BuyEventWork());
disruptor.start();
RingBuffer<BuyEvent> ringBuffer = disruptor.getRingBuffer();
BuyEventConsumer buyEventConsumer = new BuyEventConsumer(ringBuffer);
BuyEventConsumer buyEventConsumer1 = new BuyEventConsumer(ringBuffer);
BuyEventConsumer buyEventConsumer2 = new BuyEventConsumer(ringBuffer);
Semaphore semaphore = new Semaphore(50);
CountDownLatch countDownLatch = new CountDownLatch(100000);
Long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
final int num = i;
threadPoolExecutor.execute(() -> {
try {
semaphore.acquire();
buyEventConsumer.makeOrder("微信000" + num, Long.valueOf(1 + random.nextInt(3)),
1 + random.nextInt(10));
buyEventConsumer1.makeOrder("淘寶000" + num, Long.valueOf(1 + random.nextInt(3)),
1 + random.nextInt(20));
buyEventConsumer2.makeOrder("京東000" + num, Long.valueOf(1 + random.nextInt(3)),
1 + random.nextInt(15));
} catch (Exception e) {
e.printStackTrace();
}
semaphore.release();
countDownLatch.countDown();
});
}
countDownLatch.await();
threadPoolExecutor.shutdown();
disruptor.shutdown();
log.info("耗時:{}ms", System.currentTimeMillis() - start);
// 最後統計order列表數據總和爲多少
lists.stream().collect(Collectors.groupingBy(BuyEventWork.Order::getGoodsId,
Collectors.summarizingInt(BuyEventWork.Order::getCount)))
.forEach((id, data) -> log.info("ordersId:{} 統計數據:{}", id, data));
}
@Setter
@Getter
static class BuyEvent {
private String accout;
private Integer count;
private Long goodsId;
}
/**
* 購物系統
*/
static class BuyEventWork implements WorkHandler<BuyEvent> {
@Getter
@Setter
@AllArgsConstructor
static class Goods {
private Integer sum;
private String name;
}
@Getter
@Setter
@AllArgsConstructor
static class Order {
private String name;
private String goodsName;
private Integer count;
private Long goodsId;
}
private static final Map<Long, Goods> goodsMap = new HashMap<>();
static {
goodsMap.put(1L, new Goods(100, "長城7日遊"));
goodsMap.put(2L, new Goods(500, "跨年演唱會門票"));
goodsMap.put(3L, new Goods(600, "春節大禮炮"));
}
@Override
public void onEvent(BuyEvent buyEvent) throws Exception {
checkStockAndOrder(buyEvent);
}
public synchronized void checkStockAndOrder(BuyEvent buyEvent) {
Long goodsId = buyEvent.getGoodsId();
if (!goodsMap.containsKey(goodsId)) {
log.info("用戶:{},抱歉,您要購買的物品已下架", buyEvent.getAccout());
return;
}
Goods goods = goodsMap.get(goodsId);
if (buyEvent.getCount() > goods.getSum()) {
log.info("用戶:{},抱歉,您要購買的[{}份{}]庫存不足,當前剩餘{}份", buyEvent.getAccout(), buyEvent.getCount(),
goods.getName(),
goods.getSum());
return;
}
goods.setSum(goods.getSum() - buyEvent.getCount());
goodsMap.put(goodsId, goods);
lists.add(new Order(buyEvent.getAccout(), goods.getName(), buyEvent.getCount(), goodsId));
log.info("用戶:{},恭喜您,[{}份{}]下單成功", buyEvent.getAccout(), buyEvent.getCount(), goods.getName());
}
}
/**
* 用戶信息處理
*/
static class BuyEventConsumer {
private final static EventTranslatorVararg<BuyEvent> translator = (BuyEvent, seq, objs) -> {
BuyEvent.setAccout(String.valueOf(objs[0]));
BuyEvent.setGoodsId(Long.valueOf(String.valueOf(objs[1])));
BuyEvent.setCount(Integer.valueOf(String.valueOf(objs[2])));
};
private final RingBuffer<BuyEvent> ringBuffer;
public BuyEventConsumer(RingBuffer<BuyEvent> ringBuffer) {
this.ringBuffer = ringBuffer;
}
public void makeOrder(String account, Long goodsId, Integer count) {
ringBuffer.publishEvent(translator, account, goodsId, count);
}
}
}
下面開始講解(實體類就沒必要介紹了哈)
1、BuyEventWork類:
可以看到他實現的是WorkHandler接口,因爲我們要使用的是Disruptor中的handleEventsWithWorkerPool方法,這個方法意思就是分組執行裏面只要有一個成員讀取到這個消息就失效了。還可以實現EventHandler接口,但是實現這個接口以後可以說所有成員讀取到纔算失效,場景適合於通知類、廣播類的場合
2、BuyEventConsumer類:
就是一個生成數據的類,就好比生產者的操作,如果是單線程可以不需要EventTranslator...
三、執行結果:
可以發現數據統計數據裏面ordersId對應的數據是100,500,600和代碼設置的一樣,還有最後提示的都是餘數爲0,所以這個demo還是差不多的,由於併發寫得不咋滴所以測試可能結果還是有點不理想,可以把他寫成http接口用壓測工具測試達到高併發的效果。
如有不對請在評論區進行評論,謝謝。
《………………………………………………菜鳥起飛中,請各位走過路過的多多指教……………………………………》