mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-05-16 16:59:24 +00:00
feat: Implement Actor Model pattern
* feat: Implement Actor Model pattern #3232 * feat: Implement Actor Model pattern #3232 * feat: update Actor Model implementation with multi-actor logic #3251 * feat: update Actor Model implementation with multi-actor logic and loose coupling #3251 * test: add unit test for actor model #3251 * test: add test for App.java to increase coverage * docs: add complete README for Actor Model pattern also implemented changes #3251
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2022 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.actormodel;
|
||||
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
public abstract class Actor implements Runnable {
|
||||
|
||||
@Setter @Getter private String actorId;
|
||||
private final BlockingQueue<Message> mailbox = new LinkedBlockingQueue<>();
|
||||
private volatile boolean active =
|
||||
true; // always read from main memory and written back to main memory,
|
||||
|
||||
// rather than being cached in a thread's local memory. To make it consistent to all Actors
|
||||
|
||||
public void send(Message message) {
|
||||
mailbox.add(message); // Add message to queue
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
active = false; // Stop the actor loop
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (active) {
|
||||
try {
|
||||
Message message = mailbox.take(); // Wait for a message
|
||||
onReceive(message); // Process it
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Child classes must define what to do with a message
|
||||
protected abstract void onReceive(Message message);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2022 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.actormodel;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class ActorSystem {
|
||||
private final ExecutorService executor = Executors.newCachedThreadPool();
|
||||
private final ConcurrentHashMap<String, Actor> actorRegister = new ConcurrentHashMap<>();
|
||||
private final AtomicInteger idCounter = new AtomicInteger(0);
|
||||
|
||||
public void startActor(Actor actor) {
|
||||
String actorId = "actor-" + idCounter.incrementAndGet(); // Generate a new and unique ID
|
||||
actor.setActorId(actorId); // assign the actor it's ID
|
||||
actorRegister.put(actorId, actor); // Register and save the actor with it's ID
|
||||
executor.submit(actor); // Run the actor in a thread
|
||||
}
|
||||
|
||||
public Actor getActorById(String actorId) {
|
||||
return actorRegister.get(actorId); // Find by Id
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
executor.shutdownNow(); // Stop all threads
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2022 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The Actor Model is a design pattern used to handle concurrency in a safe, scalable, and
|
||||
* message-driven way.
|
||||
*
|
||||
* <p>In the Actor Model: - An **Actor** is an independent unit that has its own state and behavior.
|
||||
* - Actors **communicate only through messages** — they do not share memory. - An **ActorSystem**
|
||||
* is responsible for creating, starting, and managing the lifecycle of actors. - Messages are
|
||||
* delivered asynchronously, and each actor processes them one at a time.
|
||||
*
|
||||
* <p>💡 Key benefits: - No shared memory = no need for complex thread-safety - Easy to scale with
|
||||
* many actors - Suitable for highly concurrent or distributed systems
|
||||
*
|
||||
* <p>🔍 This example demonstrates the Actor Model: - `ActorSystem` starts two actors: `srijan` and
|
||||
* `ansh`. - `ExampleActor` and `ExampleActor2` extend the `Actor` class and override the
|
||||
* `onReceive()` method to handle messages. - Actors communicate using `send()` to pass `Message`
|
||||
* objects that include the message content and sender's ID. - The actors process messages
|
||||
* **asynchronously in separate threads**, and we allow a short delay (`Thread.sleep`) to let them
|
||||
* run. - The system is shut down gracefully at the end.
|
||||
*/
|
||||
package com.iluwatar.actormodel;
|
||||
|
||||
public class App {
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
ActorSystem system = new ActorSystem();
|
||||
Actor srijan = new ExampleActor(system);
|
||||
Actor ansh = new ExampleActor2(system);
|
||||
|
||||
system.startActor(srijan);
|
||||
system.startActor(ansh);
|
||||
ansh.send(new Message("Hello ansh", srijan.getActorId()));
|
||||
srijan.send(new Message("Hello srijan!", ansh.getActorId()));
|
||||
|
||||
Thread.sleep(1000); // Give time for messages to process
|
||||
|
||||
srijan.stop(); // Stop the actor gracefully
|
||||
ansh.stop();
|
||||
system.shutdown(); // Stop the actor system
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2022 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.actormodel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class ExampleActor extends Actor {
|
||||
private final ActorSystem actorSystem;
|
||||
@Getter private final List<String> receivedMessages = new ArrayList<>();
|
||||
|
||||
public ExampleActor(ActorSystem actorSystem) {
|
||||
this.actorSystem = actorSystem;
|
||||
}
|
||||
|
||||
// Logger log = Logger.getLogger(getClass().getName());
|
||||
|
||||
@Override
|
||||
protected void onReceive(Message message) {
|
||||
LOGGER.info(
|
||||
"[{}]Received : {} from : [{}]", getActorId(), message.getContent(), message.getSenderId());
|
||||
Actor sender = actorSystem.getActorById(message.getSenderId()); // sender actor id
|
||||
// Reply of the message
|
||||
if (sender != null && !message.getSenderId().equals(getActorId())) {
|
||||
sender.send(new Message("I got your message ", getActorId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2022 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.actormodel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class ExampleActor2 extends Actor {
|
||||
private final ActorSystem actorSystem;
|
||||
@Getter private final List<String> receivedMessages = new ArrayList<>();
|
||||
|
||||
public ExampleActor2(ActorSystem actorSystem) {
|
||||
this.actorSystem = actorSystem;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onReceive(Message message) {
|
||||
receivedMessages.add(message.getContent());
|
||||
LOGGER.info("[{}]Received : {}", getActorId(), message.getContent());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2022 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.actormodel;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public class Message {
|
||||
private final String content;
|
||||
private final String senderId;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
|
||||
*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2022 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.iluwatar.actor;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import com.iluwatar.actormodel.ActorSystem;
|
||||
import com.iluwatar.actormodel.App;
|
||||
import com.iluwatar.actormodel.ExampleActor;
|
||||
import com.iluwatar.actormodel.ExampleActor2;
|
||||
import com.iluwatar.actormodel.Message;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class ActorModelTest {
|
||||
@Test
|
||||
void testMainMethod() throws InterruptedException {
|
||||
App.main(new String[] {});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMessagePassing() throws InterruptedException {
|
||||
ActorSystem system = new ActorSystem();
|
||||
|
||||
ExampleActor srijan = new ExampleActor(system);
|
||||
ExampleActor2 ansh = new ExampleActor2(system);
|
||||
|
||||
system.startActor(srijan);
|
||||
system.startActor(ansh);
|
||||
|
||||
// Ansh recieves a message from Srijan
|
||||
ansh.send(new Message("Hello ansh", srijan.getActorId()));
|
||||
|
||||
// Wait briefly to allow async processing
|
||||
Thread.sleep(200);
|
||||
|
||||
// Check that Srijan received the message
|
||||
assertTrue(
|
||||
ansh.getReceivedMessages().contains("Hello ansh"),
|
||||
"ansh should receive the message from Srijan");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user