使用两个单独的Java并发结构重新排序

时间:2013-08-22 20:07:53

标签: java concurrency

我在一个项目队列上启动一个长时间运行的进程,当一个项目被安排处理或正在处理时,我想禁止其他一些操作。我的代码基本上是这样的:

public class LongRunningProcess extends Thread {
    private final ConcurrentLinkedQueue<Item> pending = new ConcurrentLinkedQueue<>();
    private final Set<Item> active = Collections.newSetFromMap(new ConcurrentHashMap<Item, Boolean>());

    public LongRunningProcess() {
        // add items to pending; no more items will ever be added
    }

    @Override
    public void run() {
        while (! pending.isEmpty()) {
            // The peek/add/remove pattern here is important. The number
            // of items that are active or scheduled is always decreasing.
            // Because isScheduled checks pending before checking active,
            // this order of operations ensures that we never miss an item
            // as it is being switched from one collection to the other.
            Item nextItem = pending.peek();
            active.add(nextItem);    // <---Can any of these get reordered?
            pending.remove();        // <---+
            processItem(nextItem);   // <---+
            active.remove(nextItem); // <---+
        }
    }

    public boolean isScheduled(Item item) {
        return pending.contains(item) || active.contains(item);
    }
}

这会以我期望的方式工作,还是可以重新排序上面突出显示的代码块?你能指点我任何相关的规格吗?

编辑:

@ Banthar的有用评论让我看到java.util.concurrent package documentation,它最终回答了我的问题:

  

java.util.concurrent及其子包中所有类的方法将这些保证扩展到更高级别的同步。特别是:

     
      
  • 在将对象放入任何并发集合之前的线程中的操作在从另一个线程中的集合访问或删除该元素之后的之前发生的操作。
  •   

1 个答案:

答案 0 :(得分:1)

  

这是否符合我的预期,或者上述两个突出显示的项目中的任何一个都可以重新排序?你能指点我任何相关的规格吗?

简短的回答是,因为两个集合都是并发类,所以active.add(...)之后pending.remove()无法发生。{/ 1>。

  • pending.peek();pending.remove();访问volatile字段head

    private transient volatile Node<E> head = new Node<E>(null);
    
  • active.add(nextItem);访问内部锁定volatile字段:

    compareAndSetState(0, acquires)) {
    

因为两个集合都是并发类,所以它们都有内部锁或volatile变量,因此方法调用具有读/写内存屏障,可确保“发生之前”保证。这可确保由于Java Memory Model而无法重新排序操作。

但是,当您查看其他线程如何使用这两个集合时,这并不意味着您的逻辑正确或没有竞争条件。另外,这些调用不是原子的,所以你可以有3个线程:

  1. t1 - Item nextItem = pending.peek();
  2. t2 - Item nextItem = pending.peek();
  3. t1 - active.add(nextItem);
  4. t3 - 从活动中删除nextItem并处理它或其他东西
  5. t2 - active.add(nextItem);
  6. t3 - 从活动中删除nextItem并再次处理