Java哈希锁表

时间:2014-02-04 07:13:15

标签: java multithreading concurrency thread-safety locking

java中是否有一个结构或库可以用作“锁的哈希表”?

我正在存储可由多个线程访问的大量哈希值。我想在这个数组上同步替换操作。锁定索引本身不起作用,因为我正在使用一个基元数组。由于我需要的元素数量,我不想存储同样长的Objects()。相反,我希望缩小我的哈希值并根据它进行锁定。

我正在对前一个值进行更新替换条件,而不是添加,因此排除了jdk中的一些并发库。

以下是我对实施的尝试:

  private final Object[] locks = new Object[256];
  {
    for (int i = 0; i < 256; i++) {
      locks[i] = new Object();
    }
  }

  final static int p = 64;
  byte[] hashTable = new byte[1 << p];
  int populatedCount = 0;

  public void add(String s, int i) {
    int h = GetHash(s);
    int hashBucket = h >>> (32 - p);
    int lockHash = h & 0xFF;
    int oldValue;
    synchronized (locks[lockHash]) {
      oldValue = this.hashTable[hashBucket];
      this.hashTable[hashBucket] = (byte) Math.max(this.hashTable[hashBucket], i);
    }
    if (oldValue == 0) {
      this.populatedCount++;
    }
  }

我真正需要的是一个稀疏的并发原始数组......

2 个答案:

答案 0 :(得分:2)

如果理解错误,则需要以原子方式获取和替换基本数组的操作(此处:int [])。正确的吗?

如果是这样,您应该阅读AtomicInteger。这是一个完全提供这些原子操作的类。你可以制作一个AtomicInteger []。

但是 - 在探索Java API时,可以找到更好的解决方案:AtomicIntegerArray。该类的一个对象在其字段中包含一个int [],它对其值提供原子操作。


修改

嗯......在持有锁时首先进行计算(在这种情况下是最大值)的要求似乎有点棘手。

以下子类可能会起到作用:

public class MyAtomicIntegerArray extends AtomicIntegerArray {
    // constructors (see also class AtomicIntegerArray)

    public int setIfGreater(int index, int value) {
        while(true) {
            int oldValue = get(index);
            int newValue = Math.max(value, oldValue);
            if(compareAndSet(index, oldValue, newValue))
                return oldValue;
        }
    }
}

需要while(true),因为如果另一个线程在此期间更新了该值,compareAndSet方法可能会返回false。在这种情况下,我们需要再次计算。

之后,您在代码中使用此类和此方法:

private static final ARRAY_SIZE = ...;

private final MyAtomicIntegerArray hashTable = new MyAtomicIntegerArray(ARRAY_SIZE);

public void add(String s, int i) {
    int h = GetHash(s);
    int hashBucket = h >>> (32 - p);
    int oldValue = hashTable.setIfGreater(hashBucket, i);
    if (oldValue == 0) {
        this.populatedCount++;
    }
}

答案 1 :(得分:0)

我想这个解决方案更像是一个黑客,只有当你的整数键在0-255 / -128到127范围内时才有效。

您已经讨论了如何仅仅因为原语而无法对原始键进行同步。为什么不将它转换为对象呢?

获取lastHash值并对其应用Integer.valueOf()方法以获取Integer对象,然后同步Integer对象本身:

public void add(String s, int i) {
    int h = GetHash(s);
    int hashBucket = h >>> (32 - p);
    int lockHash = h & 0xFF;
    int oldValue;
    //Subtract 128 to move the range of values into the cached range of -128 to 127.
    Integer lockVal = Integer.valueOf(lockHash - 128);

    synchronized (lockVal) {
        oldValue = this.hashTable[hashBucket];
        this.hashTable[hashBucket] = (byte) Math.max(this.hashTable[hashBucket], i);
    }
    if (oldValue == 0) {
        this.populatedCount++;
    }
}

对于从-128到127的数字返回的Integer对象将始终相同,因为此处显示的Integer.valueOf()方法中定义了缓存:

  

此方法将始终缓存-128到127(包括端点)范围内的值,并可以缓存此范围之外的其他值。

在这种情况下,你在技术上得到了一系列缓存的对象,并保证在整数范围-128到127之间不会改变,因此在这些数字上调用Integer.valueOf()对你的情况来说是完美的。 / p>