From 31f0f5a904906920ae8a77fb07f1b47824222c75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Fri, 24 May 2024 09:06:42 +0300 Subject: [PATCH] docs: update active object --- active-object/README.md | 115 +++++++++++++++++++++++++++++----------- 1 file changed, 84 insertions(+), 31 deletions(-) diff --git a/active-object/README.md b/active-object/README.md index 33e330db4..f0f94aec4 100644 --- a/active-object/README.md +++ b/active-object/README.md @@ -3,7 +3,11 @@ title: Active Object category: Concurrency language: en tag: - - Performance + - Asynchronous + - Decoupling + - Messaging + - Synchronization + - Thread management --- ## Intent @@ -12,16 +16,33 @@ The Active Object design pattern provides a safe and reliable way to implement a ## Explanation -The class that implements the active object pattern will contain a self-synchronization mechanism without using 'synchronized' methods. - Real-world example -> The Orcs are known for their wildness and untameable soul. It seems like they have their own thread of control based on previous behavior. +> Imagine a busy restaurant where customers place orders with waiters. Instead of the waiters going to the kitchen to prepare the food themselves, they write the orders on slips and hand them to a dispatcher. The dispatcher manages a pool of chefs who prepare the meals asynchronously. Once a chef is free, they pick up the next order from the queue, prepare the dish, and notify the waiter when it's ready for serving. +> +> In this analogy, the waiters represent the client threads, the dispatcher represents the scheduler, and the chefs represent the method execution in separate threads. This setup allows the waiters to continue taking orders without being blocked by the food preparation process, much like the Active Object pattern decouples method invocation from execution to enhance concurrency. -To implement a creature that has its own thread of control mechanism and expose its API only and not the execution itself, we can use the Active Object pattern. +In plain words + +> The Active Object pattern decouples method execution from method invocation to improve concurrency and responsiveness in multithreaded applications. + +Wikipedia says + +> The active object design pattern decouples method execution from method invocation for objects that each reside in their own thread of control.[1] The goal is to introduce concurrency, by using asynchronous method invocation and a scheduler for handling requests. +> +> The pattern consists of six elements: +> +> * A proxy, which provides an interface towards clients with publicly accessible methods. +> * An interface which defines the method request on an active object. +> * A list of pending requests from clients. +> * A scheduler, which decides which request to execute next. +> * The implementation of the active object method. +> * A callback or variable for the client to receive the result. **Programmatic Example** +The Orcs are known for their wildness and untameable soul. It seems like they have their own thread of control based on previous behavior. To implement a creature that has its own thread of control mechanism and expose its API only and not the execution itself, we can use the Active Object pattern. + ```java public abstract class ActiveCreature { private final Logger logger = LoggerFactory.getLogger(ActiveCreature.class.getName()); @@ -95,31 +116,56 @@ public class Orc extends ActiveCreature { Now, we can create multiple creatures such as Orcs, tell them to eat and roam, and they will execute it on their own thread of control: ```java - public static void main(String[]args){ - var app=new App(); - app.run(); - } +public class App implements Runnable { -@Override -public void run(){ - ActiveCreature creature; - try{ - for(int i=0;i creatures = new ArrayList<>(); + try { + for (int i = 0; i < NUM_CREATURES; i++) { + creatures.add(new Orc(Orc.class.getSimpleName() + i)); + creatures.get(i).eat(); + creatures.get(i).roam(); + } + Thread.sleep(1000); + } catch (InterruptedException e) { + logger.error(e.getMessage()); + Thread.currentThread().interrupt(); + } finally { + for (int i = 0; i < NUM_CREATURES; i++) { + creatures.get(i).kill(0); + } } + } +} +``` + +Program output: + +``` +09:00:02.501 [Thread-0] INFO com.iluwatar.activeobject.ActiveCreature -- Orc0 is eating! +09:00:02.501 [Thread-2] INFO com.iluwatar.activeobject.ActiveCreature -- Orc2 is eating! +09:00:02.501 [Thread-1] INFO com.iluwatar.activeobject.ActiveCreature -- Orc1 is eating! +09:00:02.504 [Thread-0] INFO com.iluwatar.activeobject.ActiveCreature -- Orc0 has finished eating! +09:00:02.504 [Thread-1] INFO com.iluwatar.activeobject.ActiveCreature -- Orc1 has finished eating! +09:00:02.504 [Thread-0] INFO com.iluwatar.activeobject.ActiveCreature -- Orc0 has started to roam in the wastelands. +09:00:02.504 [Thread-2] INFO com.iluwatar.activeobject.ActiveCreature -- Orc2 has finished eating! +09:00:02.504 [Thread-1] INFO com.iluwatar.activeobject.ActiveCreature -- Orc1 has started to roam in the wastelands. +09:00:02.504 [Thread-2] INFO com.iluwatar.activeobject.ActiveCreature -- Orc2 has started to roam in the wastelands. ``` ## Class diagram -![alt text](./etc/active-object.urm.png "Active Object class diagram") +![Active Object](./etc/active-object.urm.png "Active Object class diagram") ## Applicability @@ -130,32 +176,39 @@ public void run(){ ## Tutorials -* [Android and Java Concurrency: The Active Object Pattern](https://www.youtube.com/watch?v=Cd8t2u5Qmvc) +* [Android and Java Concurrency: The Active Object Pattern(Douglas Schmidt)](https://www.youtube.com/watch?v=Cd8t2u5Qmvc) + +## Known Uses + +* Real-time trading systems where transaction requests are handled asynchronously. +* GUIs where long-running tasks are executed in the background without freezing the user interface. +* Game programming to handle concurrent updates to game state or AI computations. ## Consequences -Benefits +Benefits: * Improves responsiveness of the main thread. * Encapsulates concurrency concerns within objects. * Promotes better code organization and maintainability. * Provides thread safety and avoids shared state access problems. -Trade-offs +Trade-offs: * Introduces additional overhead due to message passing and thread management. * May not be suitable for all types of concurrency problems. ## Related patterns -* Observer -* Reactor -* Producer-consumer -* Thread pool +* [Command](https://java-design-patterns.com/patterns/command/): Encapsulates a request as an object, similarly to how the Active Object pattern encapsulates method calls. +* [Promise](https://java-design-patterns.com/patterns/promise/): Provides a means to retrieve the result of an asynchronous method call, often used in conjunction with Active Object. +* [Proxy](https://java-design-patterns.com/patterns/proxy/): The Active Object pattern can use a proxy to handle method invocations asynchronously. ## Credits * [Design Patterns: Elements of Reusable Object Software](https://amzn.to/3HYqrBE) * [Concurrent Programming in Java: Design Principles and Patterns](https://amzn.to/498SRVq) +* [Java Concurrency in Practice](https://amzn.to/4aRMruW) * [Learning Concurrent Programming in Scala](https://amzn.to/3UE07nV) * [Pattern Languages of Program Design 3](https://amzn.to/3OI1j61) +* [Pattern-Oriented Software Architecture Volume 2: Patterns for Concurrent and Networked Objects](https://amzn.to/3UgC24V)