1

我有一个在多线程环境中使用的 hi-lo Id 生成器。每个线程每秒最多可以调用 100k 次生成器

我有一个相当好的(和安全的)实现,效果很好。IdAllocator 是获取下一批“id”的对象。您可以假设这是线程安全的。我还将 batchSize 设置得很高(100 万)

private final IdAllocator idAllocator;
private final String idKey;
private final int batchSize;

private final Object lock = new Object();

private long currentId = 0;
private long nextFetchId = -1;

IdGeneratorImpl( IdAllocator idAllocator, String idKey, int batchSize )
{
    this.idAllocator = idAllocator;
    this.idKey = idKey;
    this.batchSize = batchSize;
}

public long getNextId()
{
    synchronized ( lock )
    {
        if ( currentId < nextFetchId )
        {
            long localCurrent = currentId;
            currentId++;
            return localCurrent;
        }

        currentId = idAllocator.allocateBatch( idKey, batchSize );
        nextFetchId = currentId + batchSize;

        return getNextId();
    }
}

目前,我主要(但并非总是)以无争议的方式使用它。但是将来它将被多个线程调用。

我考虑过为每个线程实例化一个实例,这可能是最好的方法。然而,作为一种智力/学习经验,我想知道我是否可以改进这个实现,特别是在多个线程频繁调用它时减少 getNextId() 中的潜在争用?

4

2 回答 2

0

如果你看一下 Hibernate TableHiLoGenerator,它在生成方法中使用了一个简单synchronized的方法,这意味着多个线程将等待,因此一次只有一个线程执行该方法。你对你的做了同样的事情lock(这有点多余 -synchronized方法也是如此)。所以我认为你的实现很好。

于 2010-08-09T22:19:27.650 回答
0

我敢打赌,你过度优化了。一百万的批量大小意味着您在每百万次插入时访问数据库。如果您改为使用一千个,您将看不到性能差异并且浪费的 ID 会少得多。当然,有了long它并不重要。

在每个线程中执行上述操作意味着您在每个线程中浪费了多达一百万个 ID。没什么大不了的long,我知道。

它还会不必要地增加启动成本,并可能留下大量线程本地垃圾。再说一次,没什么大不了的,但有什么好处呢?像上面这样的短代码需要几纳秒,而数据库访问需要更多的数量级。因此,任何争用都不会真正减慢您的速度。

但是,优化这一点的正确方法可能是通过AtomicLong. 它的开销比同步低得多。当getAndIncrement您超出nextFetchId. 在此块中,首先重新检查是否有另一个线程完成了您将要做的工作,如果没有,则从数据库中获取下一个 id。

一个更简单的方法是使用休眠HiLoOptimizer或其更好的变体。

于 2016-09-28T16:37:22.900 回答