-
-
Notifications
You must be signed in to change notification settings - Fork 27.3k
Implemented Coarse-Grained pattern issue #1289 #3124
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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/) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <module version="4"> | ||
| <component name="CheckStyle-IDEA-Module" serialisationVersion="2"> | ||
| <option name="activeLocationsIds" /> | ||
| </component> | ||
| </module> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | ||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| <modelVersion>4.0.0</modelVersion> | ||
| <parent> | ||
| <groupId>com.iluwatar</groupId> | ||
| <artifactId>java-design-patterns</artifactId> | ||
| <version>1.26.0-SNAPSHOT</version> | ||
| </parent> | ||
|
|
||
| <artifactId>coarse-grained-lock</artifactId> | ||
|
|
||
| <properties> | ||
| <maven.compiler.source>17</maven.compiler.source> | ||
| <maven.compiler.target>17</maven.compiler.target> | ||
| <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
| </properties> | ||
|
Comment on lines
+14
to
+18
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not needed, comes from parent pom.xml |
||
| <dependencies> | ||
| <dependency> | ||
| <groupId>org.testng</groupId> | ||
| <artifactId>testng</artifactId> | ||
| <version>7.10.2</version> | ||
| <scope>test</scope> | ||
| </dependency> | ||
|
Comment on lines
+20
to
+25
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why we need this? Could we stick to JUnit instead? |
||
| <dependency> | ||
| <groupId>org.junit.jupiter</groupId> | ||
| <artifactId>junit-jupiter-api</artifactId> | ||
| <scope>test</scope> | ||
| </dependency> | ||
| </dependencies> | ||
|
|
||
| </project> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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; | ||
| } | ||
| } | ||
|
Comment on lines
+68
to
+85
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use Lombok to get rid of boilerplate like getters and setters |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
| */ | ||
|
Comment on lines
+11
to
+15
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please explain the pattern briefly and describe how this example implements it |
||
|
|
||
| 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"); | ||
| }); | ||
|
Comment on lines
+18
to
+27
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add comments to the code. Remember this is learning material. |
||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| 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; | ||
| } | ||
|
|
||
| /** | ||
| * 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; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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(); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please follow the frontmatter format exactly. Here is an example from another pattern: