条件变量和锁的使用

时间:2015-12-26 20:56:17

标签: java multithreading

我目前正在尝试实现受线程保护的链接列表。实现的方法之一是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();
      }
  }

任何帮助都会很棒!感谢。

2 个答案:

答案 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,您的看跌期权将不会释放锁定。