Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * Copyright (C) 2013 The Guava Authors
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 package com.google.common.util.concurrent;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
 
 
 import  javax.annotation.concurrent.GuardedBy;
 import  javax.annotation.concurrent.ThreadSafe;

A thread-safe queue of listeners, each with an associated Executor, that guarantees that every Runnable that is added will be executed in the same order that it was added and also that execution is delayed until the next call to execute. This is particularly useful if you need to ensure that listeners are not executed with a lock held.

While similar, this is different than ExecutionList which makes much looser guarantees around ordering and has a notion of state (the executed bit) that this class does not have.

Listeners are queued by calling add and flushed on calls to execute(). Because calls to add may be concurrent with calls to execute there is no guarantee that the queue will be empty after calling execute.

Exceptions thrown by a listener will be propagated up to the executor. Any exception thrown during Executor.execute (e.g., a RejectedExecutionException or an exception thrown by inline execution) will be caught and logged.

Author(s):
Luke Sandberg
 
 @ThreadSafe final class ExecutionQueue {
   private static final Logger logger = Logger.getLogger(ExecutionQueue.class.getName());

  
The listeners to execute in order.
 
       Queues.newConcurrentLinkedQueue();
  
This lock is used with RunnableExecutorPair.submit to ensure that each listener is executed at most once.
 
   private final ReentrantLock lock = new ReentrantLock();

  
Adds the Runnable and accompanying Executor to the queue of listeners to execute.
 
   void add(Runnable runnableExecutor executor) {
     .add(new RunnableExecutorPair(runnableexecutor));
   }

  
Executes all listeners in the queue. However, note that listeners added concurrently with this method may be executed as part of this call or not, so there is no guarantee that the queue is empty after calling this method.
 
   void execute() {
     // We need to make sure that listeners are submitted to their executors in the correct order. So
     // we cannot remove a listener from the queue until we know that it has been submited to its
     // executor.  So we use an iterator and only call remove after submit.  This iterator is 'weakly
     // consistent' which means it observes the list in the correct order but not neccesarily all of
     // it (i.e. concurrently added or removed items may or may not be observed correctly by this
     // iterator).  This is fine because 1. our contract says we may not execute all concurrently
     // added items and 2. calling listener.submit is idempotent, so it is safe (and generally cheap)
     // to call it multiple times.
     while (iterator.hasNext()) {
       iterator.next().submit();
       iterator.remove();
     }
   }

  
The listener object for the queue.

This ensures that:

  1. executor.execute is called at most once
  2. runnable.run is called at most once by the executor
  3. lock is not held when runnable.run is called
  4. no thread calling submit can return until the task has been accepted by the executor
  private final class RunnableExecutorPair implements Runnable {
    private final Executor executor;
    private final Runnable runnable;
    
Should be set to true after executor.execute has been called.
    @GuardedBy("lock")
    private boolean hasBeenExecuted = false;
  
    RunnableExecutorPair(Runnable runnableExecutor executor) {
      this. = checkNotNull(runnable);
      this. = checkNotNull(executor);
    }
  
    
Submit this listener to its executor
    private void submit() {
      .lock();
      try {
        if (!) {
          try {
            .execute(this);
          } catch (Exception e) {
            .log(."Exception while executing listener " + 
                + " with executor " + e);
          }
        }
      } finally {
        // If the executor was the sameThreadExecutor we may have already released the lock, so
        // check for that here.
        if (.isHeldByCurrentThread()) {
           = true;
          .unlock();
        }
      }
    }
  
    @Override public final void run() {
      // If the executor was the sameThreadExecutor then we might still be holding the lock and
      // hasBeenExecuted may not have been assigned yet so we unlock now to ensure that we are not
      // still holding the lock while execute is called.
      if (.isHeldByCurrentThread()) {
         = true;
        .unlock();
      }
      .run();
    }
  }
New to GrepCode? Check out our FAQ X