{"id":22513428,"url":"https://github.com/emahtab/pessimistic-locking","last_synced_at":"2026-05-12T23:37:00.995Z","repository":{"id":263713322,"uuid":"891261629","full_name":"eMahtab/pessimistic-locking","owner":"eMahtab","description":"Pessimistic Locking example over MySQL database table row","archived":false,"fork":false,"pushed_at":"2024-11-20T10:07:21.000Z","size":255,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-02T03:19:11.294Z","etag":null,"topics":["concurrency","java","mysql","pessimistic-locking"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eMahtab.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-11-20T02:37:24.000Z","updated_at":"2024-11-20T10:07:24.000Z","dependencies_parsed_at":null,"dependency_job_id":"9f47d356-bf78-4953-8022-ec88f620d764","html_url":"https://github.com/eMahtab/pessimistic-locking","commit_stats":null,"previous_names":["emahtab/pessimistic-locking"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eMahtab%2Fpessimistic-locking","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eMahtab%2Fpessimistic-locking/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eMahtab%2Fpessimistic-locking/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eMahtab%2Fpessimistic-locking/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eMahtab","download_url":"https://codeload.github.com/eMahtab/pessimistic-locking/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245950635,"owners_count":20699102,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["concurrency","java","mysql","pessimistic-locking"],"created_at":"2024-12-07T03:12:20.252Z","updated_at":"2025-10-23T21:32:07.214Z","avatar_url":"https://github.com/eMahtab.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Pessimistic Locking\nPessimistic locking is used when we want **only one thread (out of multiple threads) at a time to update the database record (a row in a database table)**.\nThe thread which gets the lock over table row, gets the chance to update the record, other threads which want to update the same record waits for the, lock over table row to be released.\nA thread releases the lock by commiting the transaction or by rollback.\n\nBelow is a simple example which explains the pessimistic locking, where multiple threads (5 in this case) try to update the quantity in product_inventory table for product_id 1.\nOnly one thread at a time update the quantity for product_id 1. Other threads wait until, the thread which have the lock releases the lock over table row with product_id 1.\n\nAlso the program doesn't allow the updateProductInventory() operation where the update would result in setting a value which is less than zero for quantity. It throws an **InsufficientProductInventoryException**.\n\n## Step 1 : Create product_inventory table under test database in MySQL\n```sql\nCREATE TABLE `test`.`product_inventory` (\n  `product_id` INT NOT NULL,\n  `product_name` VARCHAR(500) NULL,\n  `quantity` INT NULL,\n  PRIMARY KEY (`product_id`));\n```\n## Step 2 : Insert a product record in the product_inventory table\nWe insert a product with product_id as 1 and quantity set to 5\n```sql\nINSERT INTO product_inventory(product_id,product_name,quantity)\nVALUES (1,'Some Popular Product',5);\n```\n\n### Step 3 : Write the code, making sure no two threads update the same product at the same time\nIn the below Java program we simulate 5 different threads trying to update the quanity for the product whose product_id is 1.\n\nNote the Prepared statement `selectForUpdate`, by adding `FOR UPDATE` at the end of SELECT statement, we are trying to get the lock for the update on the specific row (in this case product with product_id 1).\n```java\nString selectForUpdate = \"SELECT quantity FROM \"+ PRODUCT_INVENTORY_TABLE +\" WHERE product_id = ? FOR UPDATE\";\n```\n\n**The program throws InsufficientProductInventoryException if we try to set the product quanity to a negative value (less than zero).**\n\n![\"Eclipse Project\"](eclipse-project.png?raw=true)\n\n```java\npackage net.mahtabalam;\n\nimport java.sql.Connection;\nimport java.sql.DriverManager;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\n\npublic class Test {\n\n    private static final String DB_URL = \"jdbc:mysql://localhost:3306/test?useSSL=false\";\n    private static final String DB_USER = \"root\";\n    private static final String DB_PASSWORD = \"YOUR_DB_USER_PASSWORD\";\n    private static final String PRODUCT_INVENTORY_TABLE = \"product_inventory\";\n\n    public static void main(String[] args) {\n        // Simulating 5 threads trying to update the same product\n        Thread t1 = new Thread(() -\u003e updateProductInventory(1, -1));\n        Thread t2 = new Thread(() -\u003e updateProductInventory(1, -2));\n        Thread t3 = new Thread(() -\u003e updateProductInventory(1, -2));\n        Thread t4 = new Thread(() -\u003e updateProductInventory(1, 2));\n        Thread t5 = new Thread(() -\u003e updateProductInventory(1, -1));\n\n        t1.start(); t2.start(); t3.start(); t4.start(); t5.start();\n    }\n\n    private static void updateProductInventory(int productId, int quantityChange) {\n    \tSystem.out.println(Thread.currentThread().getName()+\" entered updateProductInventory() execution :\"+quantityChange);\n    \t\n        try (Connection connection = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD)) {\n            connection.setAutoCommit(false); // Begin transaction\n\n            // Lock the specific row\n            String selectForUpdate = \"SELECT quantity FROM \"+ PRODUCT_INVENTORY_TABLE +\" WHERE product_id = ? FOR UPDATE\";\n            try (PreparedStatement selectStmt = connection.prepareStatement(selectForUpdate)) {\n                selectStmt.setInt(1, productId);\n                ResultSet rs = selectStmt.executeQuery();\n\n                if (rs.next()) {\n                    int currentQuantity = rs.getInt(\"quantity\");\n                    int newQuantity = currentQuantity + quantityChange;\n                    // Ensure quantity does not go below zero\n                    if (newQuantity \u003c 0) {\n                        throw new InsufficientProductInventoryException(\n                        \t\tThread.currentThread().getName()+\" Insufficient inventory for product ID \" + productId + \". \" +\n                                \"Current quantity: \" + currentQuantity + \", attempted change: \" + quantityChange);\n                    }\n\n                    // Update the row with new quantity\n                    String updateQuery = \"UPDATE \"+ PRODUCT_INVENTORY_TABLE +\" SET quantity = ? WHERE product_id = ?\";\n                    try (PreparedStatement updateStmt = connection.prepareStatement(updateQuery)) {\n                        updateStmt.setInt(1, newQuantity);\n                        updateStmt.setInt(2, productId);\n                        updateStmt.executeUpdate();\n                    }\n\n                    System.out.println(Thread.currentThread().getName() + \" updated product \" + productId + \n                                       \" to quantity: \" + newQuantity);\n                } else {\n                    System.out.println(\"Product with ID \" + productId + \" not found.\");\n                }\n\n                connection.commit(); // Commit transaction\n            } catch (InsufficientProductInventoryException e) {\n                connection.rollback(); // Rollback transaction\n                throw e; // Rethrow the exception to notify the caller\n            } catch (Exception e) {\n                connection.rollback(); // Rollback transaction in case of an error\n                System.err.println(Thread.currentThread().getName() + \" encountered an error: \" + e.getMessage());\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n}\n\npublic class InsufficientProductInventoryException extends Exception{\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic InsufficientProductInventoryException(String message) {\n\t\tsuper(message);\n    }\n}\n```\n\n### Code Execution Outputs :\n**Note that at the start of every run, I manually set the quanity of product_id 1 to 5 with an update statement**\n```sql\nUPDATE product_inventory set quantity= 5 where product_id = 1\n```\n#### Output 1:\n```\nThread-4 entered updateProductInventory() execution :-1\nThread-0 entered updateProductInventory() execution :-1\nThread-1 entered updateProductInventory() execution :-2\nThread-3 entered updateProductInventory() execution :2\nThread-2 entered updateProductInventory() execution :-2\nThread-1 updated product 1 to quantity: 3\nThread-2 updated product 1 to quantity: 1\nThread-3 updated product 1 to quantity: 3\nThread-0 updated product 1 to quantity: 2\nThread-4 updated product 1 to quantity: 1\n```\n#### Output 2:\n```\nThread-4 entered updateProductInventory() execution :-1\nThread-1 entered updateProductInventory() execution :-2\nThread-2 entered updateProductInventory() execution :-2\nThread-0 entered updateProductInventory() execution :-1\nThread-3 entered updateProductInventory() execution :2\nThread-1 updated product 1 to quantity: 3\nThread-3 updated product 1 to quantity: 5\nThread-0 updated product 1 to quantity: 4\nThread-4 updated product 1 to quantity: 3\nThread-2 updated product 1 to quantity: 1\n```\n\n#### Output 3:\n```\nThread-4 entered updateProductInventory() execution :-1\nThread-3 entered updateProductInventory() execution :2\nThread-1 entered updateProductInventory() execution :-2\nThread-2 entered updateProductInventory() execution :-2\nThread-0 entered updateProductInventory() execution :-1\nThread-0 updated product 1 to quantity: 4\nThread-2 updated product 1 to quantity: 2\nThread-4 updated product 1 to quantity: 1\nnet.mahtabalam.InsufficientProductInventoryException: Thread-1 Insufficient inventory for product ID 1. Current quantity: 1, attempted change: -2\n\tat net.mahtabalam.Test.updateProductInventory(Test.java:44)\n\tat net.mahtabalam.Test.lambda$1(Test.java:18)\n\tat java.base/java.lang.Thread.run(Thread.java:1575)\nThread-3 updated product 1 to quantity: 3\n```\nBy the above output results, its clear that regardless of the order of threads being successful in getting the lock over table row, **no two threads are able to update the product quantity for the same product at the same time**. This ensures consistency of the product quantity, which is very important in any real world application, and guarantees that each thread will see the correct value of quantity before updating value of quantity. \n\nYou would never want that multiple users bought the same product when the inventory had just 1 quantity of that product, or multiple users being able to book the exact same seat when it really is just one physical seat. Pessimistic locking is a reliable mechanism to enforce data integrity in high-contention scenarios.\n\n**Pessimistic locking is a better choice when possibility of a conflict is high.**\n\n## References :\nOptimistic Locking : https://github.com/eMahtab/optimistic-locking\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femahtab%2Fpessimistic-locking","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Femahtab%2Fpessimistic-locking","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femahtab%2Fpessimistic-locking/lists"}