Use of the queue interface in java

  • 2020-05-17 05:31:10
  • OfStack

The Queue interface is at the same level as List and Set, both of which inherit the Collection interface. LinkedList implements the Queue interface. The Queue interface narves access to LinkedList's methods (that is, if the parameter type in the method is Queue, you can only access the methods defined by the Queue interface, not the non-Queue methods of LinkedList directly) so that only the appropriate methods can be used. BlockingQueue inherits the Queue interface.

A queue is a data structure. It has two basic operations: add 1 element, at the end of the queue and head to remove an element from the queue is, queue management data in the form of a kind of first in first out, if you tried to one is full of one element is added to the blocking queue, or from 1 air blocking the queue to remove 1 yuan, will cause the thread block. Blocking queues is a useful tool when multiple threads are collaborating. Worker threads can periodically store intermediate results in a blocking queue while other worker thread threads pull them out and modify them in the future. The queue will automatically balance the load. If the first set of threads runs slower than the second, the second set will block while waiting for the result. If the first set of threads is running fast, it will wait for the second set to catch up. The following table shows the blocking queue operation in jdk 1.5:

If the queue is full, an IIIegaISlabEepeplian exception is thrown
remove removes and returns the element at the head of the queue. If the queue is empty, an NoSuchElementException exception is thrown
element returns the element at the head of the queue and throws an NoSuchElementException exception if the queue is empty
offer adds an element and returns true if the queue is full, false is returned
poll removes and returns the element at the head of the queue. If the queue is empty, null is returned
peek returns the element at the head of the queue. If the queue is empty, null is returned
put adds an element if the queue is full, it blocks
take removes and returns the element at the head of the queue. If the queue is empty, it is blocked

remove, element, offer, poll, peek actually belong to the Queue interface.

Blocking queue operations can be divided into three categories according to how they respond: aad, removee, and element operations throw exceptions when you try to add an element to a full queue or get an element from an empty queue. Of course, in multithreaded programs, queues can become full or empty at any time, so you might want to use the offer, poll, peek methods. These methods give only one error indication when a task cannot be completed without throwing an exception.

Note: the poll and peek methods failed to return null. Therefore, it is illegal to insert null values into the queue.

There are also variants of the offer and poll methods with timeouts, for example, the following call:


boolean success = q.offer(x,100,TimeUnit.MILLISECONDS);

Try to insert an element to the end of the queue in 100 milliseconds. If successful, return true immediately; Otherwise, when timeout is reached, return false. Similarly, call:


Object head = q.poll(100, TimeUnit.MILLISECONDS);

If the queue header element is successfully removed within 100 milliseconds, the header element is returned immediately. Otherwise, when a timeout is reached, null is returned.

Finally, we have blocking operations put and take. The put method blocks when the queue is full, and the take method blocks when the queue is empty.

The java.ulil.concurrent package provides four variants of the blocking queue. By default, LinkedBlockingQueue has no upper limit on its capacity (not exactly, Integer.MAX_VALUE if not specified, but you can also choose to specify its maximum capacity, which is a list-based queue that sorts elements by FIFO (first in first out).

When ArrayBlockingQueue is constructed, it needs to specify the capacity and can choose whether it needs fairness. If the fairness parameter is set to true, the thread with the longest wait will be processed first (this fairness is achieved by setting ReentrantLock to true: that is, the thread with the longest wait will operate first). Often, fairness costs you in performance and you only use it when you really need it. It is an array-based blocking loop queue that sorts the elements according to the FIFO (first in first out) principle.

PriorityBlockingQueue is a queue with priority, not a fifo queue. The elements are removed in priority order, and the queue has no upper limit (see the source code 1). PriorityBlockingQueue is a repackaging of PriorityQueue, which is based on heap data structure, while PriorityQueue has no capacity limit, like ArrayList1, so put will not be blocked on the priority blocking queue. Although this queue is logically unbounded, an attempt to perform an add operation may result in OutOfMemoryError) because the resource is exhausted, but if the queue is empty, the fetch operation take blocks, so its retrieval operation take is blocked. In addition, the elements entering the queue must have comparative capabilities.

Finally, DelayQueue (implemented on the basis of PriorityQueue) is an unbounded blocking queue holding Delayed elements, from which elements can be extracted only when the delay expires. The head of the queue is the Delayed element that is saved the longest after the delay expires. If the delays have not yet expired, the queue has no head, and poll returns null. When an element's getDelay(TimeUnit.NANOSECONDS) method returns a value less than or equal to zero, the expiration occurs and poll removes the element. The null element is not allowed for this queue. Here is the delay interface:

Java code


public interface Delayed extends Comparable<Delayed> { 
   long getDelay(TimeUnit unit); 
} 

The element put in DelayQueue will also implement the compareTo method, which DelayQueue USES to sort the elements.

The following example shows how to use a blocking queue to control a set of threads. The program searches all files in one directory and all its subdirectories and prints out a list of files containing the specified keywords. As can be seen from the following example, two significant benefits of using blocking queues are that there is no need for additional synchronization when multiple threads operate on a common queue, and that the queue will automatically balance the load, which means that the processing speed gap between the two sides (production and consumption sides) will be blocked when processing is faster. Here is the implementation:

Java code


public class BlockingQueueTest { 
  public static void main(String[] args) { 
    Scanner in = new Scanner(System.in); 
    System.out.print("Enter base directory (e.g. /usr/local/jdk5.0/src): "); 
    String directory = in.nextLine(); 
    System.out.print("Enter keyword (e.g. volatile): "); 
    String keyword = in.nextLine(); 
 
    final int FILE_QUEUE_SIZE = 10;//  Block queue size  
    final int SEARCH_THREADS = 100;//  Number of keyword search threads  
 
    //  Based on the ArrayBlockingQueue Block queue of  
    BlockingQueue<File> queue = new ArrayBlockingQueue<File>( 
        FILE_QUEUE_SIZE); 
 
    // Just start 1 Two threads to search the directory  
    FileEnumerationTask enumerator = new FileEnumerationTask(queue, 
        new File(directory)); 
    new Thread(enumerator).start(); 
     
    // Start the 100 Two threads are used to search the file for the specified keyword  
    for (int i = 1; i <= SEARCH_THREADS; i++) 
      new Thread(new SearchTask(queue, keyword)).start(); 
  } 
} 
class FileEnumerationTask implements Runnable { 
  // The dummy metafile object, placed at the end of the blocking queue, is used to indicate that the file has been traversed  
  public static File DUMMY = new File(""); 
 
  private BlockingQueue<File> queue; 
  private File startingDirectory; 
 
  public FileEnumerationTask(BlockingQueue<File> queue, File startingDirectory) { 
    this.queue = queue; 
    this.startingDirectory = startingDirectory; 
  } 
 
  public void run() { 
    try { 
      enumerate(startingDirectory); 
      queue.put(DUMMY);// Execution to this point indicates that the specified directory file has been traversed  
    } catch (InterruptedException e) { 
    } 
  } 
 
  //  Will specify all files in the directory to File The form of an object is placed in a blocking queue  
  public void enumerate(File directory) throws InterruptedException { 
    File[] files = directory.listFiles(); 
    for (File file : files) { 
      if (file.isDirectory()) 
        enumerate(file); 
      else 
        // Put the element at the end of the queue and block it if the queue is full  
        queue.put(file); 
    } 
  } 
} 
class SearchTask implements Runnable { 
  private BlockingQueue<File> queue; 
  private String keyword; 
 
  public SearchTask(BlockingQueue<File> queue, String keyword) { 
    this.queue = queue; 
    this.keyword = keyword; 
  } 
 
  public void run() { 
    try { 
      boolean done = false; 
      while (!done) { 
        // Fetches the queue header element and blocks if the queue is empty  
        File file = queue.take(); 
        if (file == FileEnumerationTask.DUMMY) { 
          // Pull it out and put it back in so that other threads can read it quickly  
          queue.put(file); 
          done = true; 
        } else 
          search(file); 
      } 
    } catch (IOException e) { 
      e.printStackTrace(); 
    } catch (InterruptedException e) { 
    } 
  } 
  public void search(File file) throws IOException { 
    Scanner in = new Scanner(new FileInputStream(file)); 
    int lineNumber = 0; 
    while (in.hasNextLine()) { 
      lineNumber++; 
      String line = in.nextLine(); 
      if (line.contains(keyword)) 
        System.out.printf("%s:%d:%s%n", file.getPath(), lineNumber, 
            line); 
    } 
    in.close(); 
  } 
} 

Links: the original http: / / www cnblogs. com end/archive 2012/10/25/2738493. html


Related articles: