Refactoring synchronization blocks keeping ordering of execution intact

落爺英雄遲暮 提交于 2021-01-29 10:29:32

问题


I have this synchronized block in one of my classes, whose main responsibility is to capture metrics around service calls. All variables prefixed with _ are class variables and are primitive longs. I am looking to refactor this synchronized block so as to increase the throughput, but keeping the overall accuracy intact. I have a test setup which calls this method from Runnable threads using an executor service. I am doing a comparison on the metrics before and after refactoring.

public CallCompletion startCall()
{
  long currentTime;
  Pending pending;

  synchronized (_lock)
  {
    currentTime = _clock.currentTimeMillis();
    _tracker.getStatsWithCurrentTime(currentTime);
    _lastStartTime = currentTime;
    _sumOfOutstandingStartTimes += currentTime;

    _callStartCountTotal++;
    _tracker._callStartCount++;
    if (_callStartsInLastSecondTracker != null)
      _callStartsInLastSecondTracker.addCall();
    _concurrency++;
    if (_concurrency > _tracker._concurrentMax)
    {
      _tracker._concurrentMax = _concurrency;
    }
    pending = checkForPending();
  }
  if (pending != null)
  {
    pending.deliver();
  }
  return new CallCompletionImpl(currentTime);
}

I tried few approaches like:

i) Breaking into multiple locks -

public CallCompletion startCall()
{
  long currentTime;
  Pending pending;

  synchronized (_lock)
  {
    currentTime = _clock.currentTimeMillis();
    _tracker.getStatsWithCurrentTime(currentTime);
    _lastStartTime = currentTime;
    _sumOfOutstandingStartTimes += currentTime;
  }

  synchronization(_lock1) {
    _callStartCountTotal++;
    _tracker._callStartCount++;
    if (_callStartsInLastSecondTracker != null)
      _callStartsInLastSecondTracker.addCall();
    _concurrency++;
    if (_concurrency > _tracker._concurrentMax)
    {
      _tracker._concurrentMax = _concurrency;
    }
    pending = checkForPending();
  }
  if (pending != null)
  {
    pending.deliver();
  }
  return new CallCompletionImpl(currentTime);
}

ii) Using AtomicLong for _lastStartTime:

public CallCompletion startCall()
{
  long currentTime;
  Pending pending;
  long lastStartTime = _lastStartTime.get();

  if (_callStartsInLastSecondTracker != null) {
    synchronized (_lock) {
      _callStartsInLastSecondTracker.addCall();
    }
  }

  currentTime = _clock.currentTimeMillis();
  if (_lastStartTime.compareAndSet(lastStartTime, currentTime)) {
    _tracker.getStatsWithCurrentTime(currentTime);
    _sumOfOutstandingStartTimes += currentTime;
    _callStartCountTotal++;
    _tracker._callStartCount++;
    _concurrency++;
    if (_concurrency > _tracker._concurrentMax) {
      _tracker._concurrentMax = _concurrency;
    }
  }

  pending = checkForPending();
  if (pending != null)
  {
    pending.deliver();
  }
  return new CallCompletionImpl(currentTime);
}

While the throughput does seem to increase as seen in my benchmarks, but the overall metrics accuracy seem to drop when comparing with the original synchronized method. Somewhere, I might be messing up with the ordering of the executions. Can someone help me out with this?

来源:https://stackoverflow.com/questions/61959538/refactoring-synchronization-blocks-keeping-ordering-of-execution-intact

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!