Java 带压头锁的比赛条件?
我正在尝试为Hibernate实现自己的序列生成器。开箱即用的方法附带了一个同步方法,这会在我的应用程序中造成太多的争用(多个线程并行地将数据插入Oracle数据库) 我本来想尝试一下StampedLock,但不幸的是,我的测试用例(150000行,16个线程)总是在150000次执行中产生5-15次id冲突 附上我的代码,你知道我做错了什么吗?或者你能建议一个更好的方法吗?多谢各位Java 带压头锁的比赛条件?,java,oracle,hibernate,sequence,java.util.concurrent,Java,Oracle,Hibernate,Sequence,Java.util.concurrent,我正在尝试为Hibernate实现自己的序列生成器。开箱即用的方法附带了一个同步方法,这会在我的应用程序中造成太多的争用(多个线程并行地将数据插入Oracle数据库) 我本来想尝试一下StampedLock,但不幸的是,我的测试用例(150000行,16个线程)总是在150000次执行中产生5-15次id冲突 附上我的代码,你知道我做错了什么吗?或者你能建议一个更好的方法吗?多谢各位 import java.io.Serializable; import java.util.concurrent
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() );
}
}
这段代码不是线程安全的
this.sequenceValue = new AtomicLong( lastSourceValue );
在最坏的情况下,您将使用相同的
值,其中N是正在运行的线程数。我用一个AtomicLong替换了IntegralDataTypeHolder upperLimitValue,解决了这个问题。但是只有在获得写锁时才调用此代码,因此,没有其他线程可以运行它?这不是问题的关键-问题是,在某些罕见的情况下,序列可能会恢复为lastSourceValue,而不应该恢复。最有可能的是,这就是发生在你这一方的冲突因为它不是易变的,所以可能会发生很多神奇的事情。