Bird
Raised Fist0

Library Management System

LLDmediumInterview freq: medium
Choose your preparation mode4 modes available
๐ŸŽฏ
Library Management System Design
40 min ยท medium
medium

Design a system to manage library books, member registrations, book lending with due dates, fine calculations for late returns, and reservation queues for unavailable books.

  • Books can be borrowed by members and have due dates for return.
  • Members can reserve books that are currently lent out.
  • Fines are calculated based on the number of days a book is overdue.
  • The system must prevent double lending of the same book copy.
  • Reservations are handled in a FIFO queue per book.
๐Ÿ“‹
Guided Steps
1/5
Step 1
Define Book and BookCopy Classes
Design classes to represent books and their physical copies, ensuring each copy can be uniquely tracked and lent independently.
๐Ÿ’ก Hint
Use an abstract class or concrete class with instance fields to hold state like copyId and availability status.
Use concrete class BookCopy with volatile boolean isAvailable to track availability; volatile ensures visibility of changes across threads without locking.
Mid: Defines Book and BookCopy classes with composition relationship. Senior: Explains volatile usage to prevent stale reads of isAvailable in concurrent lending scenarios.
โš ๏ธCommon Wrong Answer
Using a single Book class without distinguishing copies leads to inability to track individual copy availability, causing double lending bugs.
Step 2
Design Member and Membership Management
Create a Member class to represent library users and manage their borrowing and reservation records.
๐Ÿ’ก Hint
Member should hold collections of borrowed BookCopies and Reservations; use synchronized methods to update these collections atomically.
Use synchronized methods like public synchronized boolean borrowBook(BookCopy copy) to atomically check availability and update member's borrowed list, preventing race conditions.
Mid: Member class with collections and synchronized methods. Senior: Traces thread interleaving where two threads try to borrow the same copy; synchronized prevents inconsistent state.
โš ๏ธCommon Wrong Answer
Not synchronizing borrowBook allows two threads to borrow the same copy simultaneously, causing data corruption and double lending.
Step 3
Implement Due Dates and Fine Calculation
Model due dates for borrowed books and calculate fines for overdue returns.
๐Ÿ’ก Hint
Use java.time.LocalDate for due dates; calculate fine as a function of days overdue. Store dueDate as a final field in a Loan class.
Use immutable LocalDate dueDate in Loan class to avoid concurrency issues; fine calculation method is stateless and thread-safe.
Mid: Loan class with dueDate and fine calculation. Senior: Explains immutability of LocalDate prevents race conditions in concurrent fine queries.
โš ๏ธCommon Wrong Answer
Using mutable Date objects for dueDate leads to inconsistent fine calculations under concurrent access.
Step 4
Design Reservation Queue per Book
Implement a FIFO reservation queue for each book to handle requests when all copies are lent out.
๐Ÿ’ก Hint
Use java.util.concurrent.ConcurrentLinkedQueue for thread-safe reservation queue; synchronize enqueue and dequeue operations if using non-concurrent collections.
Use ConcurrentLinkedQueue<Reservation> for reservations to allow lock-free thread-safe enqueue/dequeue operations, preventing deadlocks and race conditions.
Mid: Reservation queue per book. Senior: Explains how ConcurrentLinkedQueue avoids race conditions without explicit synchronization.
โš ๏ธCommon Wrong Answer
Using non-thread-safe Queue without synchronization causes lost or duplicated reservations under concurrent access.
Step 5
Handle Book Lending and Returning Workflow
Design methods to lend a book copy to a member and handle returns, updating availability and notifying next reservation.
๐Ÿ’ก Hint
Use synchronized methods in Library class to atomically check availability, update BookCopy status, and notify next reservation.
Use synchronized public boolean lendBookCopy(Member m, BookCopy copy) to atomically check isAvailable and update state; volatile isAvailable ensures visibility; notify next reservation after return.
Mid: Lending and returning methods with synchronization. Senior: Traces thread interleaving where two threads attempt lending same copy; synchronized prevents double lending.
โš ๏ธCommon Wrong Answer
Not synchronizing lendBookCopy leads to race conditions where multiple members borrow the same copy simultaneously.
๐Ÿ”Œ
API Design Challenge
Design RESTful APIs to support book borrowing, returning, reservation, and fine payment operations with concurrency safety.
  • Use optimistic locking or synchronized methods to prevent double lending.
  • Design endpoints for members to view borrowed books, due dates, and fines.
  • Include APIs to enqueue and dequeue reservations atomically.
๐Ÿ“Š
Scoring Rubric
โ–ผ Show
Must Have - 60%
  • BookCopy class with volatile isAvailable field
  • Member class with synchronized borrowBook and returnBook methods
  • Loan class with immutable LocalDate dueDate
  • Reservation queue implemented with ConcurrentLinkedQueue
  • Synchronized lendBookCopy and returnBookCopy methods in Library class
Good to Have - 30%
  • Use of enum for BookStatus to avoid String-based bugs
  • Clear composition and aggregation relationships with lifecycle ownership
  • Fine calculation method is stateless and thread-safe
  • Proper exception handling for unavailable books
  • Use of Java 8+ time API for date handling
Bonus - 10%
  • Implement notification system for reservation availability
  • Support for multiple membership types with different borrowing limits
  • Use of AtomicInteger for counting available copies
  • Integration with persistent storage interfaces
  • Unit tests demonstrating concurrency correctness
Using String for book status leads to typo bugs and no compile-time checks, causing runtime errors and maintenance issues.
Not using volatile for isAvailable causes stale reads and double lending due to visibility issues in concurrent environments.
Using non-thread-safe collections for reservations causes lost or duplicated entries under concurrent access, leading to inconsistent reservation state.
Not synchronizing borrow/return methods leads to race conditions and inconsistent state, risking data corruption and double lending.
Mutable Date objects for due dates cause concurrency bugs in fine calculation due to shared mutable state.
โœ…
Model Answer
โ–ผ Reveal

The design models Books and their physical copies distinctly, using volatile boolean isAvailable in BookCopy to ensure visibility of availability status across threads. Members maintain synchronized methods to borrow and return books, preventing race conditions. Loans encapsulate immutable LocalDate dueDate fields to safely calculate fines without concurrency issues. Each Book maintains a thread-safe ConcurrentLinkedQueue for reservations, ensuring FIFO order without explicit locks. The Library class synchronizes lending and returning workflows to atomically update states and notify reservations.

  • Use of volatile boolean isAvailable in BookCopy to ensure visibility of availability status across threads without locking.
  • Synchronized methods in Member and Library classes to prevent race conditions during borrow, return, and lending operations.
  • Immutable LocalDate dueDate in Loan class to avoid concurrency issues in fine calculation.
  • ConcurrentLinkedQueue for reservation queues to allow lock-free thread-safe enqueue and dequeue operations.
  • Use of enum BookStatus instead of String to prevent runtime bugs and enable exhaustive switch statements.
  • Composition and aggregation relationships clearly defined to respect SOLID principles and lifecycle ownership.
  • Avoidance of abstract classes for BookCopy due to lack of inheritance needs; concrete class suffices.
  • Fine calculation implemented as stateless and thread-safe, adhering to Single Responsibility Principle.
  • Combination of volatile and synchronized ensures both visibility and atomicity in concurrent lending workflows.
  • Use concrete BookCopy class with volatile boolean isAvailable to track copy availability with visibility guarantees across threads.
  • Member class uses synchronized methods to atomically update borrowed books and reservations, preventing race conditions.
  • Loan class holds immutable LocalDate dueDate to avoid concurrency issues in fine calculation.
  • Reservation queue per Book uses ConcurrentLinkedQueue for thread-safe FIFO reservation management without explicit locks.
  • Library class methods lendBookCopy and returnBookCopy are synchronized to atomically update BookCopy availability and notify reservations.
  • Use enum BookStatus { AVAILABLE, BORROWED } instead of String to prevent runtime bugs and enable exhaustive switch statements.
  • Composition relationship: Library contains Books; Book contains BookCopies; Member aggregates Loans and Reservations; lifecycle ownership is clear and respects SOLID principles.
  • Abstract class not used for BookCopy because no inheritance hierarchy needed; concrete class suffices with instance state.
  • Fine calculation is stateless and thread-safe, following Single Responsibility Principle.
  • Use of volatile and synchronized together ensures visibility and atomicity in concurrent lending and returning workflows.

Practice

(1/5)
1. In a Job Scheduler design, a Job class has a private constructor and a static synchronized getInstance() method. Which design pattern is applied here and why is this the correct use case?
easy
A. Singleton pattern, to ensure only one instance of a Job exists for global scheduling control.
B. Prototype pattern, to clone existing Job instances for reuse.
C. Builder pattern, to construct complex Job objects step-by-step.
D. Factory Method pattern, because it controls object creation with a static method.

Solution

  1. Step 1: Identify the pattern from constructor and method

    A private constructor prevents external instantiation, and a static synchronized getInstance() method controls instance creation, which matches Singleton pattern characteristics.
  2. Step 2: Reason why Singleton fits Job Scheduler

    Singleton ensures a single Job instance manages scheduling globally, avoiding conflicts or duplicate scheduling logic.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Private constructor + static synchronized getInstance() -> Singleton [check]
Hint: Private constructor + static getInstance() -> Singleton [check]
Common Mistakes:
2. In designing a notification channel abstraction, should NotificationChannel be an abstract class or an interface? Consider that each channel has state like connection status and common methods like connect() and send(). Why?
easy
A. Abstract class, because it can hold shared state and provide default method implementations.
B. Abstract class, because interfaces cannot define method signatures.
C. Interface, because Java interfaces can now have default methods with state variables.
D. Interface, because it allows multiple inheritance and all methods are stateless contracts.

Solution

  1. Step 1: Analyze state and behavior requirements

    NotificationChannel needs to hold connection status (state) and provide common method implementations.
  2. Step 2: Choose between interface and abstract class

    Interfaces cannot hold instance state; abstract classes can hold state and provide default behavior, making abstract class the correct choice.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Abstract class supports shared state and default methods [check]
Hint: Stateful base -> abstract class; stateless contract -> interface [check]
Common Mistakes:
3. In the in-memory file system, the createFile() method checks if a filename is available and then adds the file to the directory without synchronization. Which exact thread interleaving causes two threads to create files with the same name (double creation)?
medium
A. Thread 1 checks filename availability -> true, context switch; Thread 2 checks availability -> true; both add file with same name.
B. Thread 1 and Thread 2 both call createFile() simultaneously and both fail due to race condition.
C. Thread 1 adds file, then Thread 2 checks availability and finds it unavailable, so no conflict occurs.
D. Thread 1 deletes a file while Thread 2 creates a file with the same name, causing inconsistency.

Solution

  1. Step 1: Trace the check-then-act sequence without synchronization

    Thread 1 calls isNameAvailable() -> true, but before adding the file, context switches to Thread 2.
  2. Step 2: Thread 2 also calls isNameAvailable() -> true and proceeds to add the file, then Thread 1 resumes and adds its file too.

    This causes double creation due to the race condition on the availability check and add operation.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Check-then-act gap without sync -> double creation [check]
Hint: Check availability -> context switch -> both add same file [check]
Common Mistakes:
4. In the auction system, the placeBid() method checks if the auction is still open before accepting a bid. Without synchronizing placeBid(), which exact thread interleaving causes two threads to accept bids after the auction closes, resulting in inconsistent state?
medium
A. Thread 1 reads auction open -> false; Thread 2 reads auction open -> true; both reject bids.
B. Thread 1 and Thread 2 both wait on a lock; no bids accepted until lock released.
C. Thread 1 writes bid; Thread 2 reads bid; no race condition occurs because writes are atomic.
D. Thread 1 reads auction open -> true; context switch before bid recorded; Thread 2 reads auction open -> true; both record bids after auction closed.

Solution

  1. Step 1: Identify check-then-act race

    Without synchronization, Thread 1 reads auction open as true but is preempted before recording the bid.
  2. Step 2: Thread 2 reads auction open as true and proceeds to record bid as well, both bypassing the closed state check.

  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Unsynchronized check-then-act -> double acceptance after close [check]
Hint: Check-then-act without sync -> race condition double accept [check]
Common Mistakes:
5. In LinkedIn's ConnectionManager, the method addConnection(User a, User b) checks if users are already connected before adding. Without synchronizing this method, what exact thread interleaving causes a double connection to be created?
medium
A. Thread 1 checks isConnected(a,b) -> false, context switch; Thread 2 checks isConnected(a,b) -> false; both add connections.
B. Thread 1 and Thread 2 both call addConnection simultaneously, but one fails due to a database constraint.
C. Thread 1 adds connection, then Thread 2 removes it before Thread 1 commits.
D. Thread 1 and Thread 2 both read connection list after addition, causing stale cache.

Solution

  1. Step 1: Identify the check-then-act race condition

    Without synchronization, both threads can see no existing connection and proceed to add, causing duplicates.
  2. Step 2: Trace exact thread interleaving

    Thread 1 calls isConnected(a,b) and gets false, then context switches before adding. Thread 2 also calls isConnected(a,b) and gets false, then both add connections concurrently.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Check-then-act gap causes double connection addition [check]
Hint: Check-then-act without sync -> double add race [check]
Common Mistakes:
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard