我目前正在尝试实现受线程保护的链接列表。实现的方法之一是take(),它删除最近添加的元素,并在列表为空时等待添加元素。我对Java线程和同步的了解非常粗制滥用,所以我想知道这个实现是否有效。特别是,我想知道如果在put()之前调用take()会发生什么。因为它们都使用相同的锁对象,我觉得如果在put()之前调用了take(),则take()的锁永远不会被释放,因此put()将被试图获取该lock()。这是我的实现
//Globals
private final Lock lock = new ReentrantLock();
final Condition notEmpty = lock.newCondition();
public boolean put(int address) {
Node newnode = new Node(null, null, address);
lock.lock();
if(first == null) {
try {
first = newnode;
last = newnode;
size++;
notEmpty.signal();
}
finally {
lock.unlock();
}
}
else {
if(contains(address) == true) {
remove(address);
}
first.prev = newnode;
newnode.next = first;
first = newnode;
size++;
}
return true;
}
拍摄
public int take() throws InterruptedException {
lock.lock();
try{
while(size() == 0)
notEmpty.await();
remove();
}
finally {
lock.unlock();
}
}
任何帮助都会很棒!感谢。
答案 0 :(得分:1)
每当put(..)
方法在非空列表上执行时(意思是first != null
),锁永远不会被释放,因为在put(..)
方法的实现中你{{{ 1}} lock()
语句之前的锁定,只有在条件为真时释放它,如果不是(执行进入if(first == null)
),你永远不会释放锁定,阻止任何其他线索将来会接受它。
您必须确保在每次执行中尽快释放锁定。
答案 1 :(得分:1)
我认为你应该考虑使用“java.util.Stack”。
Java堆栈已经同步并且有方法pop()和push(),您可以从take()和put()调用它们而不必担心锁定。
现在你需要做的就是在take()中等待堆栈对象是空的,你可以为堆栈对象设置一个监听器,或者你启动一个类参数
int takeCalled = 0
每次用空堆栈调用take()时增加它:
public void take(){
if (stackObj.isempty()) takeCalled++;
else stackObj.pop();
}
然后执行简单的put()检查以清空堆栈,如果takeCalled > 0
public void put(x){
stackObj.push(x);
while(!stackObj.isEmpty()) take();
}
我总是喜欢在实现中使用synchronized build,因为你不能让bug自由逻辑。例如,如果first != null
,您的看跌期权将不会释放锁定。