近期看了一篇文章,关于高并发下通过双缓存分组锁去提高服务并发能力。
双缓存:双缓存的目的是,如果单缓冲区满的时候,需要同步把缓冲的数据刷进磁盘,这个过程需要等很久,这妨碍到了程序的效率。通过双缓存,当某一个缓冲满了的时候,切换成另一个缓冲区,满的缓冲去可以慢慢去磁盘写数据。
文档地址:https://mp.weixin.qq.com/s/d4qfu2MxESc1YJV4Ud5mnA
自己写了个小例子(代码不足,望大侠们指点),测试过后,效率是有质的飞跃。
上图为最终的实现方案,用的是别人的图。
import org.sean.framework.pool.ExecutorPool;
import org.sean.framework.util.NumberUtil;
import java.nio.DoubleBuffer;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
/**
* 实现双缓存分组锁
*/
public class DoubleBufferTest {
/**
* 单缓存
*/
private static final Map<Integer, DoubleBuffer> doubleBufferMap = new HashMap<>();
/**
* 分组数量
*/
private static final int size = 1000;
/**
* 每个分组设置空间大小
*/
private static final int doubleSize = 100;
/**
* 爽缓存
*/
private static final Map<Integer, DoubleBufferBeanMap> doubleBuffer2Map = new HashMap<>();
static {
//分组操作
for (int i = 0; i < size; i++) {
doubleBufferMap.put(i, DoubleBuffer.allocate(doubleSize));
doubleBuffer2Map.put(i, new DoubleBufferBeanMap());
}
}
public static void main(String[] args) throws Exception {
int num = 100000;
//单缓存分组锁实现
// long t0 = System.currentTimeMillis();
// CountDownLatch count1 = new CountDownLatch(num);
// for (int i = 0; i < num; i++) {
// ExecutorPool.getInstance().execute(() -> {
// DoubleBuffer doubleBuffer = getRandomDoubleBuffer();
// try {
// synchronized (doubleBuffer) {
// if (isNotFull(doubleBuffer)) {
// task(doubleBuffer);
// }
// if (!isNotFull(doubleBuffer)) {
// flush(doubleBuffer);
// }
// }
// } catch (InterruptedException e) {
// e.printStackTrace();
// } finally {
// count1.countDown();
// }
//
// });
// }
// count1.await();
// long t1 = System.currentTimeMillis();
// System.out.println("单缓存===========>" + (t1 - t0));
//双缓存分组锁实现
long t4 = System.currentTimeMillis();
CountDownLatch count3 = new CountDownLatch(num);
for (int i = 0; i < num; i++) {
ExecutorPool.getInstance().execute(() -> {
DoubleBufferBeanMap map = getRandomDoubleBufferMap();
DoubleBuffer doubleBuffer;
try {
synchronized (map) {
doubleBuffer = map.get();
if (isNotFull(doubleBuffer)) {
task(doubleBuffer);
}
if (!isNotFull(doubleBuffer)) {
map.slide();
}
}
if (!isNotFull(doubleBuffer)) {
map.flush(doubleBuffer);
}
} catch (InterruptedException e) {
System.out.println(map);
e.printStackTrace();
} finally {
count3.countDown();
}
});
}
count3.await();
long t5 = System.currentTimeMillis();
System.out.println("双缓存===========>" + (t5 - t4));
}
/**
* 随机获取一个buffer
*
* @return {@link DoubleBuffer}
*/
private static DoubleBuffer getRandomDoubleBuffer() {
return doubleBufferMap.get(NumberUtil.getRandomInValue(size));
}
private static void task(DoubleBuffer doubleBuffer) throws InterruptedException {
doubleBuffer.put(1);
Thread.sleep(1);
}
private static void flush(DoubleBuffer doubleBuffer) throws InterruptedException {
doubleBuffer.flip();
Thread.sleep(100);
}
private static DoubleBufferBeanMap getRandomDoubleBufferMap() {
return doubleBuffer2Map.get(NumberUtil.getRandomInValue(size));
}
private static boolean isNotFull(DoubleBuffer doubleBuffer) {
return doubleBuffer.hasRemaining();
}
static class DoubleBufferBeanMap {
Map<Boolean, DoubleBuffer> doubleBufferBeans = new HashMap<>();
DoubleBufferBeanMap() {
doubleBufferBeans.put(true, DoubleBuffer.allocate(doubleSize));
doubleBufferBeans.put(false, DoubleBuffer.allocate(doubleSize));
}
void slide() {
System.out.println("切换" + doubleBufferBeans.get(true).position());
DoubleBuffer trueDoubleBuffer = doubleBufferBeans.get(true);
DoubleBuffer falseDoubleBuffer = doubleBufferBeans.get(false);
doubleBufferBeans.put(false, trueDoubleBuffer);
doubleBufferBeans.put(true, falseDoubleBuffer);
}
DoubleBuffer get() {
return doubleBufferBeans.get(true);
}
void flush(DoubleBuffer doubleBuffer) throws InterruptedException {
if (doubleBuffer.capacity() == doubleBuffer.position()) {
System.out.println("处理缓存内容" + doubleBuffer.position());
doubleBuffer.clear();
Thread.sleep(100);
}
}
}
}