Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions coarse-grained-lock/README.md
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
---
Comment on lines +1 to +12
Copy link
Owner

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:

---
title: "Abstract Document Pattern in Java: Simplifying Data Handling with Flexibility"
shortTitle: Abstract Document
description: "Explore the Abstract Document design pattern in Java. Learn its intent, explanation, applicability, benefits, and see real-world examples to implement flexible and dynamic data structures."
category: Structural
language: en
tag:
  - Abstraction
  - Decoupling
  - Dynamic typing
  - Encapsulation
  - Extensibility
  - Polymorphism
---



## 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/)
6 changes: 6 additions & 0 deletions coarse-grained-lock/coarse-grained-lock.iml
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>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
42 changes: 42 additions & 0 deletions coarse-grained-lock/etc/coarse-grained-lock.urm.puml
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
33 changes: 33 additions & 0 deletions coarse-grained-lock/pom.xml
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
Copy link
Owner

Choose a reason for hiding this comment

The 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
Copy link
Owner

Choose a reason for hiding this comment

The 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
Copy link
Owner

Choose a reason for hiding this comment

The 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
Copy link
Owner

Choose a reason for hiding this comment

The 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
Copy link
Owner

Choose a reason for hiding this comment

The 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();
}
}
}
Loading