From f6d2f4b4e41583d37a593c8ce477f9cbabe5fa4f Mon Sep 17 00:00:00 2001 From: unminnn Date: Sat, 7 Dec 2024 15:06:46 +0200 Subject: [PATCH 1/3] Added Coarse-Grained Lock pattern --- coarse-grained-lock/README.md | 105 ++++++++++++++++++ .../etc/coarse-grained-lock.urm.png | Bin 0 -> 58318 bytes .../etc/coarse-grained-lock.urm.puml | 42 +++++++ coarse-grained-lock/pom.xml | 33 ++++++ .../com/iluwatar/coarse/grained/Address.java | 85 ++++++++++++++ .../java/com/iluwatar/coarse/grained/App.java | 30 +++++ .../com/iluwatar/coarse/grained/Customer.java | 53 +++++++++ .../com/iluwatar/coarse/grained/Lock.java | 23 ++++ .../com/iluwatar/coarse/grained/AppTest.java | 73 ++++++++++++ 9 files changed, 444 insertions(+) create mode 100644 coarse-grained-lock/README.md create mode 100644 coarse-grained-lock/etc/coarse-grained-lock.urm.png create mode 100644 coarse-grained-lock/etc/coarse-grained-lock.urm.puml create mode 100644 coarse-grained-lock/pom.xml create mode 100644 coarse-grained-lock/src/main/java/com/iluwatar/coarse/grained/Address.java create mode 100644 coarse-grained-lock/src/main/java/com/iluwatar/coarse/grained/App.java create mode 100644 coarse-grained-lock/src/main/java/com/iluwatar/coarse/grained/Customer.java create mode 100644 coarse-grained-lock/src/main/java/com/iluwatar/coarse/grained/Lock.java create mode 100644 coarse-grained-lock/src/test/java/com/iluwatar/coarse/grained/AppTest.java diff --git a/coarse-grained-lock/README.md b/coarse-grained-lock/README.md new file mode 100644 index 000000000000..fde006aaa0f1 --- /dev/null +++ b/coarse-grained-lock/README.md @@ -0,0 +1,105 @@ +--- +Title: "Coarse-Grained Lock Pattern in Java: Simplifying Thread-Safe Operations" +Short Title: Coarse-Grained Lock +Description: "Coarse-Grained Lock pattern ensures thread safety by holding a global lock for related objects instead of multiple locks." +Category: Concurrency +Tags: + + Concurrency + Synchronization + Thread Safety + Locking Mechanisms +--- + + +## Intent of Coarse-Grained Lock Design Pattern + +The Coarse-Grained Lock pattern simplifies synchronization in multithreaded systems by applying a single lock to a group of operations or a shared resource. This design reduces the complexity of managing multiple fine-grained locks, albeit at the cost of some parallelism. +## Detailed Explanation of Coarse-Grained Lock Pattern with Real-World Examples + +Real-world example + +> Imagine a bank system where multiple threads handle customer transactions such as deposits, withdrawals, and balance checks. To ensure account integrity, a coarse-grained lock can be applied to the entire account object. While this reduces concurrency, it simplifies synchronization logic and prevents data inconsistencies. + +In plain words + +> A Coarse-Grained Lock is a single lock used to control access to a shared resource or related group of operations. It’s a simple way to ensure thread safety when fine-grained locking would be too complex to manage. + + +## Programmatic Example of Coarse-Grained Locks Pattern in Java + +The Coarse-Grained Lock pattern is used to synchronize access to multiple related objects. In this example, we demonstrate how to use a single lock to coordinate updates to a customer and their associated addresses. + +**Locking the Address and Customer Objects** + +```java +package com.iluwatar.coarse.grained; + +/** + * Demonstrates the Coarse-Grained Lock pattern. + */ +public class App { + + /** + * Program entry point. + * + * @param args command line arguments + */ + public static void main(String[] args) { + // Create a lock instance to synchronize access + Lock lock = new Lock(); + + // Create a customer and their addresses + Customer customer = new Customer(55, "John"); + Address address1 = new Address(customer.getCustomerId(), 1, "Chicago"); + Address address2 = new Address(customer.getCustomerId(), 2, "Houston"); + + // Use the lock to synchronize modifications to customer and addresses + lock.synchronizedMethod(() -> { + customer.setName("Smith"); + address1.setCity("Dallas"); + address2.setCity("Phoenix"); + }); + } +} + +``` +Both Addresses and customer objects can only be changed by a single thread, otherwise if another thread attempts to access a variable, then no update will occur. This example demonstrates how the Coarse-Grained Lock ensures thread-safe modifications across related objects, simplifying concurrency management while maintaining accuracy. + +## When to Use the Coarse-Grained Lock Pattern in Java + +The Coarse-Grained Lock pattern is applicable: + +* When ensuring thread safety in a multithreaded environment with shared resources. +* For applications where ease of implementation outweighs the need for high concurrency. +* When fine-grained locking introduces complexity or increases the risk of deadlocks. + +## Real-World Applications of Coarse-Grained Pattern in Java + +* Ensuring the integrity of account transactions in Banking Systems. +* Preventing concurrent threads from corrupting shared log files in Inventory Management Systems. +* Synchronizing stock operations to prevent over-selling. + +## Benefits and Trade-Offs of Coarse-Grained Lock Pattern +Benefits: + +* Easier to implement and debug compared to fine-grained locking. +* Reduces the chances of deadlocks due to fewer locks being used. +* Ensures data consistency in critical sections. + +Trade-Offs: + +* Limits the ability of threads to perform parallel operations on shared resources. +* Can lead to contention when multiple threads compete for the same lock. +* Operations that don’t require synchronization are still blocked. + +## Related Patterns + +- Readers–writer lock: allows for concurrent access for read-only operations, while requiring an exclusive lock for write operations. +- Lock Manager pattern: Can be used to define graunality in coarse-grained locks, as well as, detection and handling of deadlocks. +- Fine-Grained Lock: Increases concurrency by locking smaller portions of code or objects. +## References and Credits + +* [Java Concurrency in Practice](https://amzn.to/4cYY4kU) +* [Patterns of Enterprise Application Architecture](https://amzn.to/3Uh7rW1) +* [Oracle java concurrency](https://docs.oracle.com/javase/tutorial/essential/concurrency/) diff --git a/coarse-grained-lock/etc/coarse-grained-lock.urm.png b/coarse-grained-lock/etc/coarse-grained-lock.urm.png new file mode 100644 index 0000000000000000000000000000000000000000..3faee2e9cdb93a49fa745feaef053e1c26c062ef GIT binary patch literal 58318 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 G4g5d;oe&iO literal 0 HcmV?d00001 diff --git a/coarse-grained-lock/etc/coarse-grained-lock.urm.puml b/coarse-grained-lock/etc/coarse-grained-lock.urm.puml new file mode 100644 index 000000000000..d79f4427590e --- /dev/null +++ b/coarse-grained-lock/etc/coarse-grained-lock.urm.puml @@ -0,0 +1,42 @@ +@startuml +package com.iluwatar.coarse.grained { + + class App { + + App() + + main(args: String[]) {static} + } + + class Lock { + + synchronizedMethod(task: Runnable): void + } + + class Customer { + - customerId: int + - name: String + + Customer(customerId: int, name: String) + + setCustomerId(customerId: int): void + + getCustomerId(): int + + getName(): String + + setName(name: String): void + + } + + class Address { + - addressId: int + - customerId: int + - city: String + + Address(customerId: int, addressId: int, city: String) + + getAddressId(): int + + getCustomerId(): int + + getCity(): String + + setAddressId(addressId: int): void + + setCustomerId(customerId: int): void + + setCity(city: String): void + } + + Lock --> Customer : "Protects" + Lock --> Address : "Protects" + +} + +@enduml diff --git a/coarse-grained-lock/pom.xml b/coarse-grained-lock/pom.xml new file mode 100644 index 000000000000..6647dc56e663 --- /dev/null +++ b/coarse-grained-lock/pom.xml @@ -0,0 +1,33 @@ + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.26.0-SNAPSHOT + + + coarse-grained-lock + + + 17 + 17 + UTF-8 + + + + org.testng + testng + 7.10.2 + test + + + org.junit.jupiter + junit-jupiter-api + test + + + + \ No newline at end of file diff --git a/coarse-grained-lock/src/main/java/com/iluwatar/coarse/grained/Address.java b/coarse-grained-lock/src/main/java/com/iluwatar/coarse/grained/Address.java new file mode 100644 index 000000000000..7ae20c269009 --- /dev/null +++ b/coarse-grained-lock/src/main/java/com/iluwatar/coarse/grained/Address.java @@ -0,0 +1,85 @@ +package com.iluwatar.coarse.grained; + +/** + * Represents an address with attributes customerId, addressId, and city. + * This class associates a customer's address with a unique ID and provides + * getter and setter methods to manage its attributes. + */ +public class Address { + + /** Unique identifier for the address. */ + private int addressId; + + /** Identifier of the customer associated with the address. */ + private int customerId; + + /** The city where the address is located. */ + private String city; + + /** + * Constructs an {@code Address} object with the specified customer ID, address ID, and city. + * + * @param customerId the ID of the customer + * @param addressId the unique ID of the address + * @param city the city of the address + */ + public Address(int customerId, int addressId, String city) { + this.customerId = customerId; + this.addressId = addressId; + this.city = city; + } + + /** + * Returns the unique address ID. + * + * @return the address ID + */ + public int getAddressId() { + return addressId; + } + + /** + * Updates the address ID with the specified value. + * + * @param addressId the new address ID + */ + public void setAddressId(int addressId) { + this.addressId = addressId; + } + + /** + * Returns the ID of the customer associated with the address. + * + * @return the customer ID + */ + public int getCustomerId() { + return customerId; + } + + /** + * Updates the customer ID with the specified value. + * + * @param customerId the new customer ID + */ + public void setCustomerId(int customerId) { + this.customerId = customerId; + } + + /** + * Returns the city where the address is located. + * + * @return the city name + */ + public String getCity() { + return city; + } + + /** + * Updates the city name with the specified value. + * + * @param city the new city name + */ + public void setCity(String city) { + this.city = city; + } +} diff --git a/coarse-grained-lock/src/main/java/com/iluwatar/coarse/grained/App.java b/coarse-grained-lock/src/main/java/com/iluwatar/coarse/grained/App.java new file mode 100644 index 000000000000..7c84e75dc088 --- /dev/null +++ b/coarse-grained-lock/src/main/java/com/iluwatar/coarse/grained/App.java @@ -0,0 +1,30 @@ +package com.iluwatar.coarse.grained; +/** + * The coarse grained lock is a pattern designed to handle locking multiple objects at the same time; + * for instance, a customer can have multiple addresses and wants to change an information regarding them. + * It makes more buissenes logic to lock both the customer and the addresses in order to avoid any confuison. + * This reduces concurrent programming but ensures a more simple code that can work accurately. + */ + +public class App { + + /** + * Program entry point. + * + * @param args command line args + */ + + public static void main(String[] args) { + Lock lock = new Lock(); + Customer customer = new Customer(55, "john"); + Address address1 = new Address(customer.getCustomerId(), 1, "chicago"); + Address address2 = new Address(customer.getCustomerId(), 2, "houston"); + + lock.synchronizedMethod(() -> { + customer.setName("smith"); + address1.setCity("dallas"); + address2.setCity("phoenix"); + }); + } + +} \ No newline at end of file diff --git a/coarse-grained-lock/src/main/java/com/iluwatar/coarse/grained/Customer.java b/coarse-grained-lock/src/main/java/com/iluwatar/coarse/grained/Customer.java new file mode 100644 index 000000000000..735f76773722 --- /dev/null +++ b/coarse-grained-lock/src/main/java/com/iluwatar/coarse/grained/Customer.java @@ -0,0 +1,53 @@ +package com.iluwatar.coarse.grained; + + +public class Customer { + + + private String name; + + + private int customerId; + + + public Customer(int customerId, String name) { + this.customerId = customerId; + this.name = name; + } + + /** + * Returns the name of the customer. + * + * @return the customer's name + */ + public String getName() { + return name; + } + + /** + * Updates the name of the customer. + * + * @param name the new name of the customer + */ + public void setName(String name) { + this.name = name; + } + + /** + * Returns the unique ID of the customer. + * + * @return the customer ID + */ + public int getCustomerId() { + return customerId; + } + + /** + * Updates the unique ID of the customer. + * + * @param customerId the new customer ID + */ + public void setCustomerId(int customerId) { + this.customerId = customerId; + } +} diff --git a/coarse-grained-lock/src/main/java/com/iluwatar/coarse/grained/Lock.java b/coarse-grained-lock/src/main/java/com/iluwatar/coarse/grained/Lock.java new file mode 100644 index 000000000000..217b88327ff2 --- /dev/null +++ b/coarse-grained-lock/src/main/java/com/iluwatar/coarse/grained/Lock.java @@ -0,0 +1,23 @@ +package com.iluwatar.coarse.grained; + +/** + * Implements a coarse-grained lock for synchronizing tasks. + * This class provides a mechanism to ensure thread safety by wrapping tasks + * inside a synchronized block. + */ +public class Lock { + + /** The internal lock object used for synchronization. */ + private final Object newLock = new Object(); + + /** + * Executes a given task within a synchronized block, ensuring thread safety. + * + * @param task the {@code Runnable} task to be executed in a synchronized context + */ + public void synchronizedMethod(Runnable task) { + synchronized (newLock) { + task.run(); + } + } +} diff --git a/coarse-grained-lock/src/test/java/com/iluwatar/coarse/grained/AppTest.java b/coarse-grained-lock/src/test/java/com/iluwatar/coarse/grained/AppTest.java new file mode 100644 index 000000000000..3af2e828ea45 --- /dev/null +++ b/coarse-grained-lock/src/test/java/com/iluwatar/coarse/grained/AppTest.java @@ -0,0 +1,73 @@ +package com.iluwatar.coarse.grained; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +/** + * Unit tests for the {@link App} class. + */ +class AppTest { + + @Test + void testLockingMechanism() { + // Arrange + Lock lock = new Lock(); + Customer customer = new Customer(55, "john"); + Address address1 = new Address(customer.getCustomerId(), 1, "chicago"); + Address address2 = new Address(customer.getCustomerId(), 2, "houston"); + + // Act + lock.synchronizedMethod(() -> { + customer.setName("smith"); + address1.setCity("dallas"); + address2.setCity("phoenix"); + }); + + // Assert + assertEquals("smith", customer.getName(), "Customer name should be updated to 'smith'."); + assertEquals("dallas", address1.getCity(), "Address 1 city should be updated to 'dallas'."); + assertEquals("phoenix", address2.getCity(), "Address 2 city should be updated to 'phoenix'."); + } + + @Test + void testConcurrentModification() throws InterruptedException { + // Arrange + Lock lock = new Lock(); + Customer customer = new Customer(55, "john"); + Address address1 = new Address(customer.getCustomerId(), 1, "chicago"); + Address address2 = new Address(customer.getCustomerId(), 2, "houston"); + + // Simulate two threads attempting to modify data + Thread thread1 = new Thread(() -> lock.synchronizedMethod(() -> { + customer.setName("alice"); + address1.setCity("seattle"); + })); + + Thread thread2 = new Thread(() -> lock.synchronizedMethod(() -> { + customer.setName("bob"); + address2.setCity("miami"); + })); + + // Act + thread1.start(); + thread2.start(); + thread1.join(); + thread2.join(); + + /* + Assert + Only one thread's changes should be applied because of locking. + The output can be either customer names depending on the thread that will finish last. + both addresses will update as each thread access only one resource and not both at the same time. + */ + assertTrue( + customer.getName().equals("alice") || customer.getName().equals("bob"), + "Customer name should reflect changes from one thread only." + ); + assertEquals("seattle", address1.getCity(), + "Address cities should reflect changes from one thread only."); + + assertEquals("miami", address2.getCity(), + "Address cities should reflect changes from one thread only."); + } +} From ca3d39037ce2459b1873a160f2b32c1af83458fa Mon Sep 17 00:00:00 2001 From: unminnn Date: Sat, 7 Dec 2024 16:13:27 +0200 Subject: [PATCH 2/3] Added .iml file --- coarse-grained-lock/coarse-grained-lock.iml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 coarse-grained-lock/coarse-grained-lock.iml diff --git a/coarse-grained-lock/coarse-grained-lock.iml b/coarse-grained-lock/coarse-grained-lock.iml new file mode 100644 index 000000000000..9e3449c9d8f0 --- /dev/null +++ b/coarse-grained-lock/coarse-grained-lock.iml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file From c922bbd2b94a10715b30c3c93ceba5a8f9df38be Mon Sep 17 00:00:00 2001 From: unminnn Date: Sat, 7 Dec 2024 16:30:22 +0200 Subject: [PATCH 3/3] Updated Customer.java --- .../com/iluwatar/coarse/grained/Customer.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/coarse-grained-lock/src/main/java/com/iluwatar/coarse/grained/Customer.java b/coarse-grained-lock/src/main/java/com/iluwatar/coarse/grained/Customer.java index 735f76773722..1d465bbeb2cd 100644 --- a/coarse-grained-lock/src/main/java/com/iluwatar/coarse/grained/Customer.java +++ b/coarse-grained-lock/src/main/java/com/iluwatar/coarse/grained/Customer.java @@ -1,15 +1,23 @@ package com.iluwatar.coarse.grained; - +/** + * Represents a customer with a unique ID and a name. + * Provides methods to access and modify customer attributes. + */ public class Customer { - + /** The name of the customer. */ private String name; - + /** The unique identifier for the customer. */ private int customerId; - + /** + * Constructs a {@code Customer} object with the specified ID and name. + * + * @param customerId the unique ID of the customer + * @param name the name of the customer + */ public Customer(int customerId, String name) { this.customerId = customerId; this.name = name;