Skip to content
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

Implicitlookpatternissue #3112

Closed
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
1 change: 1 addition & 0 deletions Implicit-lock-pattern/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/target/
202 changes: 202 additions & 0 deletions Implicit-lock-pattern/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
---
title: "Implicit Lock Pattern in Java: Simplifying Concurrent Resource Access"
shortTitle: Implicit Lock
description: "Master the Implicit Lock pattern in Java to handle resource locking efficiently and safely. Learn how this design pattern promotes safe access to shared resources without direct lock management."
category: Creational
language: en
tag:
- Concurrency
- Synchronization
- Lock Management
- Multi-threading
- Resource Sharing
---

## Also known as

* Implicit Locking

## Intent of Implicit Lock Pattern

The Implicit Lock pattern in Java is designed to simplify the management of resource locking in multi-threaded environments. It provides an abstraction layer where locks are automatically handled when resources are accessed, allowing developers to focus on business logic without worrying about manual lock management.

## Detailed Explanation of Implicit Lock Pattern with Real-World Examples

### Real-world example

> Imagine a banking system where multiple users are attempting to access and modify their accounts at the same time. To avoid conflicting changes, the Implicit Lock pattern ensures that when a user accesses their account, the system automatically acquires a lock to prevent others from modifying the account simultaneously. Once the transaction is complete, the lock is released, allowing others to access the account.

### In plain words

> The Implicit Lock pattern automatically acquires and releases locks when resources are accessed, reducing the need for developers to manually manage locking.

### Wikipedia says

> The Implicit Lock pattern helps encapsulate the locking mechanisms and ensures that resources are accessed safely without manual intervention. It hides the complexity of lock management from the client code.

## Programmatic Example of Implicit Lock in Java

In this example, we simulate the access and modification of shared resources (e.g., a bank account) where the lock is implicitly managed.

### Resource Class

```java
public class Resource {
private String id;

public Resource(String id) {
this.id = id;
}

public String getId() {
return id;
}
}
```
The Resource class represents a shared resource in the system that can be locked and unlocked. It contains an id to uniquely identify the resource. This class is simple and serves as the basis for any resource that might require implicit locking in the system.

```java
// LockManager Class
public class LockManager {
private final Map<String, Lock> locks = new HashMap<>();

public boolean acquireLock(Resource resource) {
synchronized (this) {
if (!locks.containsKey(resource.getId())) {
locks.put(resource.getId(), new ReentrantLock());
}
return locks.get(resource.getId()).tryLock();
}
}

public boolean releaseLock(Resource resource) {
synchronized (this) {
Lock lock = locks.get(resource.getId());
if (lock != null && lock.isHeldByCurrentThread()) {
lock.unlock();
locks.remove(resource.getId());
return true;
}
return false;
}
}
}

```
The LockManager class is responsible for managing locks for resources. It maintains a map of resources to their corresponding locks. The acquireLock method tries to acquire a lock for a given resource. If no lock exists, it creates one. The releaseLock method releases the lock for a resource if it's held by the current thread.
```java
// Framework Class (Managing the Implicit Lock)
public class Framework {
private final LockManager lockManager;

public Framework(LockManager lockManager) {
this.lockManager = lockManager;
}

public boolean tryLockResource(Resource resource) {
return lockManager.acquireLock(resource);
}

public boolean notifyReleaseLock(Resource resource) {
return lockManager.releaseLock(resource);
}

public String loadCustomerData(Resource resource) {
return "Customer data for " + resource.getId();
}
}

```
The Framework class manages the interaction between the client code and the LockManager. It provides methods to acquire and release locks implicitly. tryLockResource tries to acquire a lock for a resource, while notifyReleaseLock releases the lock. The loadCustomerData method simulates fetching customer data for the given resource.
```java
// BusinessTransaction Class (Client Using the Framework)
public class BusinessTransaction {
private final Framework framework;

public BusinessTransaction(Framework framework) {
this.framework = framework;
}

public void processTransaction(Resource resource) {
if (framework.tryLockResource(resource)) {
System.out.println("Processing transaction for " + resource.getId());
// Simulate transaction logic
framework.notifyReleaseLock(resource);
} else {
System.out.println("Resource is locked. Try again later.");
}
}
}

```
The BusinessTransaction class represents the client code that interacts with the Framework to process transactions. It checks if a resource is available (not locked) and processes the transaction. After processing, it releases the lock. If the resource is already locked, it notifies the user to try again later

```java
// Main Class (Simulation)
public class App {
public static void main(String[] args) {
Resource resource1 = new Resource("Account1");
Resource resource2 = new Resource("Account2");

LockManager lockManager = new LockManager();
Framework framework = new Framework(lockManager);
BusinessTransaction transaction = new BusinessTransaction(framework);

transaction.processTransaction(resource1); // Successful
transaction.processTransaction(resource1); // Locked
transaction.processTransaction(resource2); // Successful
}
}


```
The App class simulates the operation of the system. It creates resources (e.g., bank accounts), initializes the LockManager and Framework, and processes transactions through the BusinessTransaction class. It demonstrates how the implicit locking mechanism works by showing a successful transaction, a locked resource, and another successful transaction.

This set of classes and their respective explanations illustrates how the Implicit Lock pattern is used to manage resource locking automatically in a multi-threaded environment, abstracting away the complexity of manual lock management.

Class Diagram
![implicit-lock.png](etc%2Fimplicit-lock.png)

When to Use the Implicit Lock Pattern in Java

Use the Implicit Lock pattern in Java when:

You need to handle concurrent access to shared resources safely.
You want to abstract the lock management to reduce boilerplate code and potential errors.
The system involves resources that must be locked and unlocked automatically without manual intervention.
You want to simplify your codebase by removing explicit lock handling.
You need to ensure that resources are accessed in a thread-safe manner, without introducing unnecessary complexity.

Benefits and Trade-offs of Implicit Lock Pattern
Benefits:

Simplicity: The lock management is abstracted away, so developers don't need to worry about handling locks explicitly.
Safety: Ensures thread-safe access to resources.
Flexibility: Allows resources to be automatically locked and unlocked when needed.
Maintainability: Reduces boilerplate code, making it easier to maintain and scale the system.

Trade-offs:

Overhead: Automatic locking and unlocking might introduce some performance overhead, especially with large numbers of resources.
Indirectness: The complexity of lock management is hidden, which can sometimes lead to challenges when debugging or understanding the exact locking behavior.
Limited Control: Since lock management is abstracted, developers may have less control over lock behavior in certain scenarios.

Real-World Applications of Implicit Lock Pattern in Java

Banking Systems: Managing access to user accounts to prevent conflicting updates during concurrent transactions.
E-commerce Platforms: Ensuring safe and consistent modification of inventory and order data when multiple users access the system simultaneously.
Database Systems: Implicit locks to ensure consistency when multiple transactions are accessing and modifying the database.
Distributed Systems: Managing resources in a distributed system where multiple nodes access the same data concurrently.

Related Java Design Patterns

Singleton: Singleton pattern often works with Implicit Lock to control global access to resources.
Factory Method: Factory Method can be used to generate instances of resources that require implicit locking.
Observer: Observer pattern can be combined with Implicit Lock to ensure thread-safe notifications.

References and Credits

Design Patterns: Elements of Reusable Object-Oriented Software
Design Patterns in Java
Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software
Java Design Patterns: A Hands-On Experience with Real-World Examples
Binary file added Implicit-lock-pattern/etc/implicit-lock.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
39 changes: 39 additions & 0 deletions Implicit-lock-pattern/etc/implicit-lock.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
@startuml
package com.iluwatar.implicitlockpattern {
class App {
+ main(args: String[]) {static}
}

class BusinessTransaction {
- framework : Framework
+ BusinessTransaction(framework: Framework)
+ processCustomer(resource: Resource, customerId: String, customerData: String)
}

class Framework {
- lockManager : LockManager
+ Framework(lockManager: LockManager)
+ tryLockResource(resource: Resource) : boolean
+ notifyReleaseLock(resource: Resource) : boolean
+ loadCustomerData(resource: Resource) : String
}

class LockManager {
- lockMap : ConcurrentHashMap<String, Lock>
+ acquireLock(resource: Resource) : boolean
+ releaseLock(resource: Resource) : boolean
}

class Resource {
- id : String
+ Resource(id: String)
+ getId() : String
}
}

App --> BusinessTransaction : "Creates"
BusinessTransaction --> Framework : "Uses"
Framework --> LockManager : "Notifies"
Framework --> Resource : "Interacts with"
LockManager --> Resource : "Locks"
@enduml
20 changes: 20 additions & 0 deletions Implicit-lock-pattern/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?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>Implicit-lock-pattern</artifactId>

<properties>
<maven.compiler.source>23</maven.compiler.source>
<maven.compiler.target>23</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.iluwatar.implicitlockpattern;


/**
* App class serves as the entry point for the simulation.
* It creates resources and processes transactions for different customers.
*/
public class App {

public static void main(String[] args) {
// Create some sample resources (could be customers, products, etc.)
Resource resource1 = new Resource("Resource1");
Resource resource2 = new Resource("Resource2");
Resource resource3 = new Resource("Resource3");

// Create a LockManager instance to manage the locks
LockManager lockManager = new LockManager();

// Create a Framework instance with the LockManager
Framework framework = new Framework(lockManager);

// Create a BusinessTransaction instance to simulate processing
BusinessTransaction transaction = new BusinessTransaction(framework);

// Process customers with their associated resources
transaction.processCustomer(resource1, "456", "Customer data for 456");
transaction.processCustomer(resource2, "123", "Customer data for 123"); // This will fail to lock
transaction.processCustomer(resource3, "789", "Customer data for 789");

// Attempting to process another customer with the same resource should fail
transaction.processCustomer(resource1, "111", "Customer data for 111"); // This will fail to lock again
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.iluwatar.implicitlockpattern;


/**
* BusinessTransaction class handles the logic of processing customer transactions.
* It works with the Framework to acquire and release locks for resources.
*/
public class BusinessTransaction {

private final Framework framework;

// Constructor accepts the Framework to interact with the LockManager
public BusinessTransaction(Framework framework) {
this.framework = framework;
}

/**
* Processes a customer transaction by acquiring a lock on the corresponding resource.
*
* @param resource the resource to be locked during the transaction
* @param customerId the ID of the customer being processed
* @param customerData the data related to the customer being processed
*/
public void processCustomer(Resource resource, String customerId, String customerData) {
// Print a message indicating which customer is being processed
System.out.println("Processing customer " + customerId + " with data: " + customerData);

// Try to acquire the lock for the resource
if (framework.tryLockResource(resource)) {
// Simulate some processing (e.g., sleeping for 500 milliseconds)
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // Handle interruption
}
// Release the lock after processing is done
framework.notifyReleaseLock(resource);
} else {
System.out.println("Failed to acquire lock for resource: " + resource.getId());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.iluwatar.implicitlockpattern;

/**
* Framework class interacts with the LockManager to acquire and release locks.
* It simplifies the usage of locking mechanisms for the BusinessTransaction.
*/
public class Framework {
private final LockManager lockManager;

// Constructor initializes the LockManager instance
public Framework(LockManager lockManager) {
this.lockManager = lockManager;
}

/**
* Requests to lock a resource via the LockManager.
*
* @param resource the resource to be locked
* @return true if the lock was acquired, false otherwise
*/
public boolean tryLockResource(Resource resource) {
return lockManager.acquireLock(resource);
}

/**
* Notifies the LockManager to release the lock on the resource.
*
* @param resource the resource to release the lock for
* @return true if the lock was released, false otherwise
*/
public boolean notifyReleaseLock(Resource resource) {
return lockManager.releaseLock(resource);
}

/**
* Simulates loading customer data.
*
* @param resource the resource to load data for
* @return customer data associated with the resource
*/
public String loadCustomerData(Resource resource) {
return "Customer data for " + resource.getId(); // Example of returning customer data
}
}
Loading
Loading