【问题标题】:Race condition with StampedLock?使用 StampedLock 的竞争条件?
【发布时间】:2019-02-10 17:12:41
【问题描述】:

我正在尝试为 Hibernate 实现我自己的序列生成器。开箱即用的方法带有同步方法,这会导致我的应用程序争用过多(多个线程将数据并行插入 Oracle 数据库)。

我想试试 StampedLock,但不幸的是,我的测试用例(16 个线程的 150000 行)在 150000 次执行中总是产生 5-15 个 id 冲突。

附上我的代码,你知道我做错了什么吗?或者你能建议一个更好的方法吗?谢谢。

import java.io.Serializable;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.StampedLock;

import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.id.IntegralDataTypeHolder;
import org.hibernate.id.SequenceGenerator;

public class HighConcurrencySequenceGenerator extends SequenceGenerator
{
  private StampedLock            lock   = new StampedLock();

  private AtomicLong             sequenceValue;   // the current generator value
  private IntegralDataTypeHolder lastSourceValue; // last value read from db
  private IntegralDataTypeHolder upperLimitValue; // the value to query the db again

  @Override
  public Serializable generate( SessionImplementor session, Object object )
  {
    long stamp = this.lock.readLock();
    try
    {
      while ( needsSequenceUpdate() )
      {
        long ws = this.lock.tryConvertToWriteLock( stamp );
        if ( ws != 0L )
        {
          stamp = ws;
          return fetchAndGetNextSequenceValue( session );
        }
        this.lock.unlockRead( stamp );
        stamp = this.lock.writeLock();
      }
      return getNextSequenceValue();
    }
    finally
    {
      this.lock.unlock( stamp );
    }
  }

  private long fetchAndGetNextSequenceValue( SessionImplementor session )
  {
    this.lastSourceValue = generateHolder( session );
    long lastSourceValue = this.lastSourceValue.makeValue()
                                               .longValue();
    this.sequenceValue = new AtomicLong( lastSourceValue );

    long nextVal = getNextSequenceValue();

    this.upperLimitValue = this.lastSourceValue.copy()
                                               .add( this.incrementSize );
    return nextVal;
  }

  private long getNextSequenceValue()
  {
    long nextVal = this.sequenceValue.getAndIncrement();
    return nextVal;
  }

  private boolean needsSequenceUpdate()
  {
    return ( this.sequenceValue == null ) || !this.upperLimitValue.gt( this.sequenceValue.get() );
  }
}

【问题讨论】:

    标签: java oracle hibernate sequence java.util.concurrent


    【解决方案1】:

    这段代码不是线程安全的

    this.sequenceValue = new AtomicLong( lastSourceValue );
    

    在最坏的情况下,您最终会得到 N 个 AtomicLong 实例 值,其中 N 是正在运行的线程数。

    【讨论】:

    • 但是这段代码只有在获得写锁的时候才会被调用,所以没有其他线程可以运行它?
    • 这不是重点 - 问题是在某些罕见的情况下,潜在的序列可能会恢复到 lastSourceValue 而它不应该是。很可能这就是你身边发生的事情,因此发生了冲突。 + 因为它不是易变的,所以可能会发生很多魔法。
    【解决方案2】:

    我用 AtomicLong 替换了 IntegralDataTypeHolder upperLimitValue,解决了这个问题。

    【讨论】:

      猜你喜欢
      • 2011-12-12
      • 2015-04-28
      • 1970-01-01
      • 1970-01-01
      • 2021-10-10
      • 2015-12-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多