【问题标题】:Use not thread-safe list for LinkedBlockingQueue对 LinkedBlockingQueue 使用非线程安全列表
【发布时间】:2015-02-22 07:23:02
【问题描述】:

我想知道如果我们将底层数据结构更改为像java.util.LinkedList 这样的非线程安全列表,为什么LinkedBlockingQueue 不能工作?当我尝试它时,我得到一个 NoSuchElementException。难道它没有被锁(takeLock)保护,这使它成为线程安全的吗?

private final List<E> list;
private final ReentrantLock takeLock;
private final ReentrantLock putLock;
private final Condition     notFull;
private final Condition     notEmpty;
private final AtomicInteger count;

public LinkedBlockingQueue() {
    takeLock = new ReentrantLock();
    putLock = new ReentrantLock();
    notFull = putLock.newCondition();
    notEmpty = takeLock.newCondition();
    count = new AtomicInteger(0);
    list = new LinkedList<E>();
}

public E get() {
   E item = null;
   int c = -1;
   try {
      takeLock.lockInterruptibly();
      while (count.get() == 0) notEmpty.await();

      // original -> item = dequeue();
      item = list.remove();   // NoSuchElementException

      c = count.getAndDecrement();
   } 
   catch (InterruptedException ie) {} 
   finally {
      takeLock.unlock();
   }

   if (c == capacity) signalNotFull();
   return item;
}

public void put(E e) {
   int c = -1;
   try {
      putLock.lockInterruptibly();
      while (count.get() == BOUND_SIZE) notFull.await();

      // original -> enqueue(node);
      list.add(e);

      c = count.getAndIncrement();
   } 
   catch (InterruptedException ie) {} 
   finally {
      putLock.unlock();
   }

   if (c == 0) signalNotEmpty();
}

【问题讨论】:

  • 您如何将项目添加到列表中?
  • countsizeputLocktakeLock有什么区别?
  • 还有,这和LinkedBlockingQueue有什么关系?
  • 您的代码看起来很不安全。 (锁不会神奇地使代码线程安全)
  • 这是为什么呢?与原始实现 (docjar.com/html/api/java/util/concurrent/…) 的唯一变化只是切换到 java.util.LinkedList

标签: java multithreading concurrency thread-safety blockingqueue


【解决方案1】:

您正在使用两个单独的锁定对象:

takeLock = new ReentrantLock();
putLock = new ReentrantLock();
notFull = putLock.newCondition();
notEmpty = takeLock.newCondition();

这是错误的。首先,您必须对 put 和 take 操作使用相同的锁对象。此外,您必须使用相同的锁定对象创建条件。

lock = new ReentrantLock();
notFull = lock.newCondition();
notEmpty = lock.newCondition();

你应该用给定的锁引用替换你的 takeLock 和 putLock 用法。

http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Lock.html http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Condition.html

【讨论】:

  • 这会起作用,但你不能同时调用 put 和 take。这就像在两种方法上都设置同步一样。当前实现 docjar.com/html/api/java/util/concurrent/… 使用 2 个锁
  • 您提到的实现是使用 2 个引用,“head”和“last”。 putLock 保护最后一个引用,而 takeLock 保护“头”引用。因此,两个锁保护两个不同的引用。 putLock 保证对“最后”引用的操作的可见性和原子性。同样,takeLock 用于“头部”参考。读者和作者之间的可见性保证已经记录在代码中。对于您的示例,您对锁中的对象进行了更改,并且您希望它对不同锁下的其他线程可见。那样不行。
  • 这完全有道理。这是我正在寻找的答案。谢谢
猜你喜欢
  • 1970-01-01
  • 2020-02-15
  • 2011-03-30
  • 1970-01-01
  • 2012-11-18
  • 2016-05-15
  • 2010-12-09
  • 2011-11-04
  • 2011-02-11
相关资源
最近更新 更多