链接: http://openjdk.java.net/projects/code-tools/jmh/.
JMH是基于注解的测试插件
引入maven依赖
IDEA安装 JMH plugin插件
打开运行程序注解配置setting->Build,…-> compiler -> Annotation Processors -> Enable Annotation Processing
定义一个类PS 处理计算
写单元测试类PSTest(运行类) 【一定写在Test-java目录下
才可以运行】
直接run运行PSTest
若出现如下异常(默认要往C盘写入测试报告,对于windows而言很私密目录不允许写)
ERROR: org.openjdk.jmh.runner.RunnerException: ERROR: Exception while trying to acquire the JMH lock (C:\WINDOWS\/jmh.lock): C:\WINDOWS\jmh.lock (拒绝访问。), exiting. Use -Djmh.ignoreLock=true to forcefully continue.
at org.openjdk.jmh.runner.Runner.run(Runner.java:216)
at org.openjdk.jmh.Main.main(Main.java:71)
点开Edit configurations->Environment variables点开->include system…勾上 (加载进入系统环境变量,系统环境变量中的TEMP目录中去写)
重新运行成功
@Benchmark 启用JMH测试
@Warmup(iterations = 1, time = 3) 预热,虚拟机先起来,调用iterations次方法,每调一次等待time秒钟,因为jvm对特定代码会优化所以预热很重要
@Fork(nums) 起nums个线程去执行这个程序
@BenchmarkMode(Mode.Throughput) 执行模式,左侧是吞吐量(每秒执行多少次)
@Measurement(iterations = 1, time = 3) 整个测试重复iterations次,每调一次等待time秒
速度最快的MQ,性能极高,内部全是cas,单机(内存中的高效率队列,跟redis)
对比ConcurrentLinkedQueue(链表实现),在遍历上来讲链表性能低于数组。
JDK中没有ConcurrentArrayQueue(数组实现),因为数组的长度是固定的,每次增加长度实际上都是新建一个更长的数组然后将数据复制进来。
总结:二进制的计算速度更快,而且转了一圈过后只会覆盖元素,所以没必要像链表一样维护头尾两个指针,只要维护一个就行了,综上所述ringBuffer的性能更强。
这里的覆盖只是原理是这样,实际上在生产中生产满了,当下一个即将覆盖的时候,发现消费者还没取走第一个商品,则不继续生产覆盖第一个,执行等待策略(一共八种,最常见的为BlockingWait,阻塞等待),等什么时候消费者拿走了数据然后唤醒生产填补空位。
这个sequence指针的实现源码有一段:
,前面声明7个long类型(8字节),后面声明7个long,保证cursor不管跟前面的对齐还是后面的对齐,一定是自己在一个缓存行内,所以效率十分高。
环形队列中的元素实际上是一个个Event对象的引用。
在new出来这个disruptor之后,会提前分配内存空间。
步骤:
定义Event,环形队列中的元素(生产的产品)
定义Event工厂,用于填充队列。
定义EventHandler(消费者),处理容器中的元素
public class LongEvent {
private long value;
public long getValue() {
return value;
}
public void setValue(long value) {
this.value = value;
}
}
public class LongEventFactory implements EventFactory {
public LongEvent newInstance() {
return new LongEvent();
}
}
public class LongEventHandler implements EventHandler {
public void onEvent(LongEvent longEvent, long l, boolean b) throws Exception {
System.out.println(longEvent.getValue());
}
}
public class LongEventProducer {
private final RingBuffer ringBuffer;
public LongEventProducer(RingBuffer ringBuffer) {
this.ringBuffer = ringBuffer;
}
public void onData(ByteBuffer buffer) {
long sequence = ringBuffer.next();
try {
LongEvent event = ringBuffer.get(sequence);
event.setValue(buffer.getLong(0));
} finally {
ringBuffer.publish(sequence);
}
}
上为声明准备类,下为主方法:
public static void main(String[] args) {
//Executor executor = Executors.newCachedThreadPool();
LongEventFactory factory = new LongEventFactory();
//must be power of 2
int ringBufferSize = 1024;
Disruptor disruptor = new Disruptor(factory, ringBufferSize, Executors.defaultThreadFactory());
disruptor.handleEventsWith(new LongEventHandler());
disruptor.start();
RingBuffer ringBuffer = disruptor.getRingBuffer();
LongEventProducer producer = new LongEventProducer(ringBuffer);
ByteBuffer bb = ByteBuffer.allocate(8);
for(long l = 0; l<100; l++) {
bb.putLong(0, l);
producer.onData(bb);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
disruptor.shutdown();
}
ProducerType生产者线程模式一共有2种:
ProducerType有两种模式 Producer.MULTI和Producer.SINGLE
默认是MULTI,表示在多线程模式下产生sequence
如果确认是单线程生产者,那么可以指定SINGLE,效率会提升
如果是多个生产者(多线程),但模式指定为SINGLE,会出什么问题呢?
①,(常用)BlockingWaitStrategy:通过线程阻塞的方式,等待生产者唤醒,被唤醒后,再循环检查依赖的sequence是否已经消费
② BusySpinWaitStrategy:线程一直自旋等待,可能比较耗cpu
③ LiteBlockingWaitStrategy:线程阻塞等待生产者唤醒,与BlockingWaitStrategy相比,区别在signalNeeded.getAndSet,如果两个线程同时访问一个访问waitfor,一个访问signalAll时,可以减少lock加锁次数
④ LiteTimeoutBlockingWaitStrategy:与LiteBlockingWaitStrategy相比,设置了阻塞时间,超过时间后抛异常
⑤ PhasedBackoffWaitStrategy:根据时间参数和传入的等待策略来决定使用哪种等待策略
⑥ TimeoutBlockingWaitStrategy:相对于BlockingWaitStrategy来说,设置了等待时间,超过后抛异常
⑦ (常用)YieldingWaitStrategy:尝试100次,然后Thread.yield()让出cpu
⑧ (常用)SleepingWaitStrategy : sleep
LongEventHandler h1 = new LongEventHandler();
LongEventHandler h2 = new LongEventHandler();
disruptor.handleEventsWith(h1, h2...)//传入多个消费者(多线程)
EventHandler h1 = (event, sequence, end) -> {
sout(event);
throw new Exception("消费者出异常");
}
disruptor.handleEventsWith(h1);
disruptor.handleExceptionsFor(h1).with(new ExcetionHandler() {
@Override
public void handleEventExceition(Throwable throwable, long l, longEvent longEvent) {
throwable.printStackTrace();
}
@Override
public void handleOnStartExceition(Throwable throwable) {
sout("Exception Start to Hanled!");
}
@Override
public void handleOnShutDownExceition(Throwable throwable) {
sout("Exception shutDown to Hanled!");
}
})
调用.handleExceptionsFor(h1).with()并重写以上三个方法,方法内处理。