我被要求执行 fine grained locking
在 hashlist
. 我已经用synchronized做了这个,但是问题告诉我要用synchronized Lock
相反。
我在构造函数中创建了一个对象的哈希列表
private LinkedList<E> data[];;
private Lock lock[];
private Lock lockR = new ReentrantLock();
// The constructors ensure that both the data and the dataLock are the same size
@SuppressWarnings("unchecked")
public ConcurrentHashList(int n){
if(n > 1000) {
data = (LinkedList<E>[])(new LinkedList[n/10]);
lock = new Lock [n/10];
}
else {
data = (LinkedList<E>[])(new LinkedList[100]);
lock = new Lock [100]; ;
}
for(int j = 0; j < data.length;j++) {
data[j] = new LinkedList<E>();
lock[j] = new ReentrantLock();// Adding a lock to each bucket index
}
}
原始方法
public void add(E x){
if(x != null){
lock.lock();
try{
int index = hashC(x);
if(!data[index].contains(x))
data[index].add(x);
}finally{lock.unlock();}
}
}
使用同步获取对象哈希列表上的句柄以允许可变 Threads
同时处理可变索引。
public void add(E x){
if(x != null){
int index = hashC(x);
synchronized (dataLock[index]) { // Getting handle before adding
if(!data[index].contains(x))
data[index].add(x);
}
}
}
我不知道如何使用 Lock
虽然我不能锁定数组中的单个元素,但只能锁定整个方法,这意味着它不是 coarse grained
.
使用数组 ReentrantLock
```
public void add(E x){
if(x != null){
int index = hashC(x);
dataLock[index].lock();
try {
// Getting handle before adding
if(!data[index].contains(x))
data[index].add(x);
}finally {dataLock[index].unlock();}
}
}
散列函数
private int hashC(E x){
int k = x.hashCode();
int h = Math.abs(k % data.length);
return(h);
}
1条答案
按热度按时间hlswsv351#
想必,
hashC()
是一个很可能产生唯一数字的函数。与中一样,您不能保证哈希是唯一的,但是非唯一哈希的发生率非常低。对于一个有几百万个条目的数据结构,你有一小部分的冲突,任何给定的冲突总是由一对或三个冲突组成(在你的数据结构中有2到3个对象有相同的散列,但不是“几千”)。另外,假设:给定对象的哈希值是常量。hashc(x)将产生相同的值,不管您调用它多少次,假设您提供相同的x。
然后,你会得到一些有趣的结论:
“水桶”(the bucket)
LinkedList
在阵列插槽中找到示例hashC(x)
在data
)你的对象应该进入,总是一样的-你知道它应该完全基于hashc的结果。计算hashc不需要任何类型的锁。它没有任何副作用。
因此,可以在不锁定任何内容的情况下知道对单个值执行给定操作所需的存储桶(可以是add、remove或check if in collection)。
现在,一旦你知道你需要看哪一个bucket/变异,好吧,现在就涉及到锁定了。
所以,每个桶只有一把锁。不是一个
List<Object> locks[];
,这是一个每桶锁的完整列表。只是Object[] locks
是你所需要的,还是ReentrantLock[] locks
如果您喜欢使用锁定/解锁而不是synchronized (lock[bucketIdx]) { ... }
.这实际上是细粒度的:毕竟,一个操作由于另一个线程正在做某事而需要旋转拇指的几率非常低,即使另一个线程正在另一个对象上操作;这将要求两个不同的对象有一个冲突的散列,这是可能的,但极为罕见-根据假设1。
注意:因此
lock
可以完全消失,您不需要它,除非您想在代码中构建代码,使代码可以完全重新设计其bucket结构。例如,如果你最终得到10亿个对象,1000个桶会让你感觉有点不舒服。不过,我不认为“反驳一切”是任务的一部分。