Состояние гонки с StampedLock?

Я пытаюсь реализовать собственный генератор последовательностей для Hibernate. Готовый вариант поставляется с синхронизированным методом, и это вызывает слишком много конфликтов в моем приложении (несколько потоков, параллельно вставляющих данные в базу данных Oracle).

Я думал, что попробую StampedLock, но, к сожалению, мои тестовые примеры (150 000 строк с 16 потоками) всегда вызывают 5-15 конфликтов идентификаторов из 150 000 выполнений.

Приложил мой код, вы хоть представляете, что я делаю не так, или вы можете предложить, может быть, лучший подход? Спасибо.

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() );
  }
}
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
0
109
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

этот фрагмент кода не является потокобезопасным

this.sequenceValue = new AtomicLong( lastSourceValue );

в худшем случае вы получите N экземпляров AtomicLong с одним и тем же значение, где N - количество запущенных потоков.

но этот код вызывается только тогда, когда была получена блокировка записи, следовательно, никакой другой поток не может его запустить?

schlangz 05.09.2018 23:02

Дело не в этом - проблема в том, что в некоторых редких случаях последовательность может быть восстановлена ​​до lastSourceValue, а этого не должно быть. Скорее всего, это то, что происходит на вашей стороне, что приводит к конфликтам. + поскольку он не изменчив, может произойти много магии.

Antoniossss 05.09.2018 23:53
Ответ принят как подходящий

Я заменил IntegralDataTypeHolder upperLimitValue на AtomicLong, решил проблему.

Другие вопросы по теме