Lecture 22: Queues and Message-Passing

来源:互联网 发布:js deepCopy 编辑:程序博客网 时间:2024/04/28 06:10

1 Message passing with threads

Strategy: Use a synchronized queue for message passing between threads. Java provides the BlockingQueue interface for queues with blocking operations:

In an ordinary Queue :

  • add(e) adds element e to the end of the queue.
  • remove() removes and returns the element at the head of the queue, or throws an exception if the queue is empty.

A BlockingQueue extends this interface:

additionally supports operations that wait for the queue to become non-empty when retrieving an element, and wait for space to become available in the queue when storing an element.

  • put(e) blocks until it can add element e to the end of the queue (if the queue does not have a size bound, put will not block).
  • take() blocks until it can remove and return the element at the head of the queue, waiting until the queue is non-empty.

notice: use put()andtake(), not use add()andremove().

1.1 producer-consumer design pattern

  • Producer threads and consumer threads share a synchronized queue.
  • Producers put data or requests onto the queue, and consumers remove and process them.
  • One or more producers and one or more consumers might all be adding and removing items from the same queue.

Java provides two implementations of BlockingQueue:

  • ArrayBlockingQueue is a fixed-size queue that uses an array representation. put ting a new item on the queue will block if the queue is full.
  • LinkedBlockingQueue is a growable queue using a linked-list representation. If no maximum capacity is specified, the queue will never fill up, so put will never block.

2 Implementing message passing with queues

  • The message in the queue must be an immutable type.
  • we must design our messages here to prevent race conditions and enable clients to perform the atomic operations they need. For example, withdraw-if-sufficient-funds would be a better operation than just withdraw.

3 Stopping

  • One strategy is a poison pill: a special message on the queue that signals the consumer of that message to end its work.
  • It is also possible to interrupt a thread by calling its interrupt() method.
    • If the thread is blocked waiting, the method it’s blocked in will throw an InterruptedException (that’s why we have to try-catch that exception almost any time we call a blocking method).
    • If the thread was not blocked, an interrupted flag will be set. The thread must check for this flag to see whether it should stop working.
      for example:
public void run() {    // handle requests until we are interrupted    while ( ! Thread.interrupted()) {        try {            // block until a request arrives            int x = in.take();            // compute the answer and send it back            int y = x * x;            out.put(new SquareResult(x, y));        } catch (InterruptedException ie) {            // stop            break;        }    }}

4 Thread safety arguments with message passing

  • Existing threadsafe data types for the synchronized queue. This queue is definitely shared and definitely mutable, so we must ensure it is safe for concurrency.
  • Immutability of messages or data that might be accessible to multiple threads at the same time.
  • Confinement of data to individual producer/consumer threads. Local variables used by one producer or consumer are not visible to other threads, which only communicate with one another using messages in the queue.
  • Confinement of mutable messages or data that are sent over the queue but will only be accessible to one thread at a time. This argument must be carefully articulated and implemented. But if one module drops all references to some mutable data like a hot potato as soon as it puts them onto a queue to be delivered to another thread, only one thread will have access to those data at a time, precluding concurrent access.

Reference

[1] 6.005 — Software Construction on MIT OpenCourseWare | OCW 6.005 Homepage at https://ocw.mit.edu/ans7870/6/6.005/s16/

0 0