Files
java-design-patterns/poison-pill
Ilkka Seppälä 6cd2d0353a docs: Content SEO updates (#2990)
* update yaml frontmatter format

* update abstract document

* update abstract factory

* use the new pattern template

* acyclic visitor seo

* adapter seo

* ambassador seo

* acl seo

* aaa seo

* async method invocation seo

* balking seo

* bridge seo

* builder seo

* business delegate and bytecode seo

* caching seo

* callback seo

* chain seo

* update headings

* circuit breaker seo

* client session + collecting parameter seo

* collection pipeline seo

* combinator SEO

* command seo

* cqrs seo

* commander seo

* component seo

* composite seo

* composite entity seo

* composite view seo

* context object seo

* converter seo

* crtp seo

* currying seo

* dao seo

* data bus seo

* data locality seo

* data mapper seo

* dto seo

* decorator seo

* delegation seo

* di seo

* dirty flag seo

* domain model seo

* double buffer seo

* double checked locking seo

* double dispatch seo

* dynamic proxy seo

* event aggregator seo

* event-based asynchronous seo

* eda seo

* event queue seo

* event sourcing seo

* execute around seo

* extension objects seo

* facade seo

* factory seo

* factory kit seo

* factory method seo

* fanout/fanin seo

* feature toggle seo

* filterer seo

* fluent interface seo

* flux seo

* flyweight seo

* front controller seo

* function composition seo

* game loop seo

* gateway seo

* guarded suspension seo

* half-sync/half-async seo

* health check seo

* hexagonal seo

* identity map seo

* intercepting filter seo

* interpreter seo

* iterator seo

* layers seo

* lazy loading seo

* leader election seo

* leader/followers seo

* lockable object seo

* rename and add seo for marker interface

* master-worker seo

* mediator seo

* memento seo

* metadata mapping seo

* microservice aggregator seo

* api gw seo

* microservices log aggregration seo

* mvc seo

* mvi seo

* mvp seo

* mvvm seo

* monad seo

* monitor seo

* monostate seo

* multiton seo

* mute idiom seo

* naked objects & notification seo

* null object seo

* object mother seo

* object pool seo

* observer seo

* optimistic locking seo

* page controller seo

* page object seo

* parameter object seo

* partial response seo

* pipeline seo

* poison pill seo

* presentation model seo

* private class data seo

* producer-consumer seo

* promise seo

* property seo

* prototype seo

* proxy seo

* queue-based load leveling seo

* reactor seo

* registry seo

* repository seo

* RAII seo

* retry seo

* role object seo

* saga seo

* separated interface seo

* serialized entity seo

* serialized lob seo

* servant seo

* server session seo

* service layer seo

* service locator seo

* service to worker seo

* sharding seo

* single table inheritance seo

* singleton seo

* spatial partition seo

* special case seo

* specification seo

* state seo

* step builder seo

* strangler seo

* strategy seo

* subclass sandbox seo

* table module seo

* template method seo

* throttling seo

* tolerant reader seo

* trampoline seo

* transaction script seo

* twin seo

* type object seo

* unit of work seo

* update method seo

* value object seo

* version number seo

* virtual proxy seo

* visitor seo

* seo enhancements

* seo improvements

* SEO enhancements

* SEO improvements

* SEO additions

* SEO improvements

* more SEO improvements

* rename hexagonal + SEO improvements

* SEO improvements

* more SEO stuff

* SEO improvements

* SEO optimizations

* SEO enhancements

* enchance SEO

* improve SEO

* SEO improvements

* update headers
2024-06-08 19:54:44 +03:00
..
2019-12-07 18:03:49 +02:00
2024-05-23 18:40:52 +03:00
2022-09-14 23:22:24 +05:30
2024-06-08 19:54:44 +03:00


title: "Poison Pill Pattern in Java: Gracefully Terminating Multithreaded Processes" shortTitle: Poison Pill description: "Explore the Poison As an alternative to "Poison Pill" design pattern in Java, used for gracefully shutting down multi-threaded applications. Understand its intent, applicability, and see a real-world example. Perfect for developers looking to enhance concurrency and messaging systems." category: Concurrency language: en tag:

  • Decoupling
  • Fault tolerance
  • Messaging
  • Thread management

Also known as

  • Shutdown Signal

Intent of Poison Pill Design Pattern

The Poison Pill design pattern is used to gracefully shut down a service or a producer-consumer system by sending a special message (the "poison pill") to message queue which indicates that no more messages will be sent, allowing the consumers to terminate.

Detailed Explanation of Poison Pill Pattern with Real-World Examples

Real-world example

A real-world analogy for the Poison Pill design pattern is the use of a "closed" sign in a retail store. When the store is ready to close for the day, the manager places a "closed" sign on the door. This sign acts as a signal to any new customers that no more customers will be admitted, but it doesn't immediately force out the customers already inside. The store staff will then attend to the remaining customers, allowing them to complete their purchases before finally locking up and turning off the lights. Similarly, in the Poison Pill pattern, a special "poison pill" message signals consumers to stop accepting new tasks while allowing them to finish processing the current tasks before shutting down gracefully.

In plain words

Poison Pill is a known message structure that ends the message exchange.

Programmatic Example of Poison Pill Pattern in Java

In this Java example, the Poison Pill serves as a shutdown signal within message queues, demonstrating effective thread management and consumer communication.

Let's define the message structure first. There's interface Message and implementation SimpleMessage.

public interface Message {
    
  // Other properties and methods...

  enum Headers {
    DATE, SENDER
  }

  void addHeader(Headers header, String value);

  String getHeader(Headers header);

  Map<Headers, String> getHeaders();

  void setBody(String body);

  String getBody();
}

public class SimpleMessage implements Message {

  private final Map<Headers, String> headers = new HashMap<>();
  private String body;

  @Override
  public void addHeader(Headers header, String value) {
    headers.put(header, value);
  }

  @Override
  public String getHeader(Headers header) {
    return headers.get(header);
  }

  @Override
  public Map<Headers, String> getHeaders() {
    return Collections.unmodifiableMap(headers);
  }

  @Override
  public void setBody(String body) {
    this.body = body;
  }

  @Override
  public String getBody() {
    return body;
  }
}

To pass messages we are using message queues. Here we define the types related to the message queue: MqPublishPoint, MqSubscribePoint and MessageQueue. SimpleMessageQueue implements all these interfaces.

public interface MqPublishPoint {
  void put(Message msg) throws InterruptedException;
}

public interface MqSubscribePoint {
  Message take() throws InterruptedException;
}

public interface MessageQueue extends MqPublishPoint, MqSubscribePoint {}

public class SimpleMessageQueue implements MessageQueue {

  private final BlockingQueue<Message> queue;

  public SimpleMessageQueue(int bound) {
    queue = new ArrayBlockingQueue<>(bound);
  }

  @Override
  public void put(Message msg) throws InterruptedException {
    queue.put(msg);
  }

  @Override
  public Message take() throws InterruptedException {
    return queue.take();
  }
}

Next, we need message Producer and Consumer. Internally they use the message queues from above. It's important to notice that when Producer stops, it sends out the poison pill to inform Consumer that the messaging has finished.

public class Producer {

  // Other properties and methods...

  public void send(String body) {
    if (isStopped) {
      throw new IllegalStateException(String.format(
          "Producer %s was stopped and fail to deliver requested message [%s].", body, name));
    }
    var msg = new SimpleMessage();
    msg.addHeader(Headers.DATE, new Date().toString());
    msg.addHeader(Headers.SENDER, name);
    msg.setBody(body);

    try {
      queue.put(msg);
    } catch (InterruptedException e) {
      // allow thread to exit
      LOGGER.error("Exception caught.", e);
    }
  }

  public void stop() {
    isStopped = true;
    try {
      queue.put(Message.POISON_PILL);
    } catch (InterruptedException e) {
      // allow thread to exit
      LOGGER.error("Exception caught.", e);
    }
  }
}

public class Consumer {

  // Other properties and methods...

  public void consume() {
    while (true) {
      try {
        var msg = queue.take();
        if (Message.POISON_PILL.equals(msg)) {
          LOGGER.info("Consumer {} receive request to terminate.", name);
          break;
        }
        var sender = msg.getHeader(Headers.SENDER);
        var body = msg.getBody();
        LOGGER.info("Message [{}] from [{}] received by [{}]", body, sender, name);
      } catch (InterruptedException e) {
        // allow thread to exit
        LOGGER.error("Exception caught.", e);
        return;
      }
    }
  }
}

Finally, we are ready to present the whole example in action.

  public static void main(String[] args) {
    var queue = new SimpleMessageQueue(10000);

    final var producer = new Producer("PRODUCER_1", queue);
    final var consumer = new Consumer("CONSUMER_1", queue);

    new Thread(consumer::consume).start();

    new Thread(() -> {
        producer.send("hand shake");
        producer.send("some very important information");
        producer.send("bye!");
        producer.stop();
    }).start();
}

Program output:

07:43:01.518 [Thread-0] INFO com.iluwatar.poison.pill.Consumer -- Message [hand shake] from [PRODUCER_1] received by [CONSUMER_1]
07:43:01.520 [Thread-0] INFO com.iluwatar.poison.pill.Consumer -- Message [some very important information] from [PRODUCER_1] received by [CONSUMER_1]
07:43:01.520 [Thread-0] INFO com.iluwatar.poison.pill.Consumer -- Message [bye!] from [PRODUCER_1] received by [CONSUMER_1]
07:43:01.520 [Thread-0] INFO com.iluwatar.poison.pill.Consumer -- Consumer CONSUMER_1 receive request to terminate.

Detailed Explanation of Poison Pill Pattern with Real-World Examples

Poison Pill

When to Use the Poison Pill Pattern in Java

Use the Poison Pill idiom when:

  • Systems require robust fault tolerance and seamless consumer shutdown in multithreaded environments.
  • In producer-consumer scenarios where consumers need to be informed about the end of message processing.
  • To ensure that consumers can finish processing remaining messages before shutting down.

Real-World Applications of Poison Pill Pattern in Java

  • Java ExecutorService shutdown using a special task to signal shutdown.
  • Messaging systems where a specific message indicates the end of the queue processing.
  • Akka framework

Benefits and Trade-offs of Poison Pill Pattern

Benefits:

  • Simplifies the shutdown process of consumers.
  • Ensures that all pending tasks are completed before termination.
  • Decouples the shutdown logic from the main processing logic.

Trade-offs:

  • Requires consumers to check for the poison pill, adding some overhead.
  • If not managed properly, could lead to consumers not recognizing the poison pill, causing indefinite blocking.
  • Producer-Consumer: Works in tandem with the Poison Pill pattern to handle the communication and shutdown of consumers.
  • Message Queue: Often uses poison pills to signal the end of message processing in the queue.
  • Observer: Can be used to notify subscribers about the shutdown event.

References and Credits