Leaf
Leaf copied to clipboard
这个并发取号逻辑是否 可以这样 进一步优化?
在getIdFromSegmentBuffer 方法中, 假设 当前Segment已经没有可用id了,当前线程A 启动线程B 准备NextSegment。 假设此时已经有100个线程阻塞在取号的方,具体也就是waitAndSleep 方法的地方。 当线程B准备就绪NextSegment后, 线程C及时发现SegmentBuffer准备就绪并先获取到了cpu,这个时候线程C会执行Segment的切换工作。切换之后将SegmentBuffer的nextReady设置为false。 对于线程A和其他被阻塞的线程而言,当这些线程 从waitAndSleep方法返回 后 需要 依次获取写锁,然后从当前Segment中取号。显然这些线程没必要依次获取写锁,而且获取SegmentBuffer写锁 会影响 另外的其他尚未被阻塞线程并发使用读锁从当前Segment中获取id。这是第一种情况。
第二种情况就是,假设线程B 准备nextSegment失败了,此时已经有了100个线程处于阻塞状态waitAndSleep,那么这100个线程会依次获取写锁并检查发现SegmentBuffer的nextReady为false,然后返回 取号失败的结果 这些线程就退出了,因此触发nextSegment的准备工作需要 等待下一次取号请求。
按照我的理解:对于唤醒后的线程,如果SegmentBuffer的nextReady为false 则当前线程 没必要通过获取读锁来取号。 只需要走正常的取号流程就可以了。这个时候对于第二种情况也会有改善,因为走了正常的取号流程 也就相当于会启动新的线程执行nextSegment的初始化工作,而不是直接返回取号失败结果。
public Result getIdFromSegmentBuffer(final SegmentBuffer buffer) {
while (true) {
buffer.rLock().lock();
try {
final Segment segment = buffer.getCurrent();
if (!buffer.isNextReady() && (segment.getIdle() < 0.9 * segment.getStep()) && buffer.getThreadRunning().compareAndSet(false, true)) {
service.execute(new Runnable() {
@Override
public void run() {
Segment next = buffer.getSegments()[buffer.nextPos()];
boolean updateOk = false;
try {
updateSegmentFromDb(buffer.getKey(), next);
updateOk = true;
logger.info("update segment {} from db {}", buffer.getKey(), next);
} catch (Exception e) {
logger.warn(buffer.getKey() + " updateSegmentFromDb exception", e);
} finally {
if (updateOk) {
buffer.wLock().lock();
buffer.setNextReady(true);
buffer.getThreadRunning().set(false);
buffer.wLock().unlock();
} else {
buffer.getThreadRunning().set(false);
}
}
}
});
}
long value = segment.getValue().getAndIncrement();
if (value < segment.getMax()) {
return new Result(value, Status.SUCCESS);
}
} finally {
buffer.rLock().unlock();
}
waitAndSleep(buffer);
//这个地方优化
buffer.wLock().lock();
try {
final Segment segment = buffer.getCurrent();
long value = segment.getValue().getAndIncrement();
if (value < segment.getMax()) {
return new Result(value, Status.SUCCESS);
}
if (buffer.isNextReady()) {
buffer.switchPos();
buffer.setNextReady(false);
} else {
//这个地方优化
logger.error("Both two segments in {} are not ready!", buffer);
return new Result(EXCEPTION_ID_TWO_SEGMENTS_ARE_NULL, Status.EXCEPTION);
}
} finally {
buffer.wLock().unlock();
}
}
}
@thelight1 @Yaccc #https://github.com/Meituan-Dianping/Leaf/pull/191
(只需要走正常的取号流程就可以了。这个时候对于第二种情况也会有改善)您指的是在调用一次getIdFromSegmentBuffer方法吗