30 Java Scenario-Based Interview Questions
A doctor's exam doesn't only ask what a disease is. It hands the student a patient: "fever for three days, a rash on the arms, just back from a trip. What do you do?" Interviews for Java developers have moved the same way. Alongside "what is a HashMap?" you will hear "our report shows the same customer twice; why?" These are scenario questions, and they test whether you can apply what you know to a real problem. The 30 scenarios on this page come from the kind of projects Java runs every day: banking, online shopping, payments and production support.
How to Answer Any Scenario
A strong answer follows the same five steps, and every model answer on this page uses them:
- Restate the problem in one sentence, so the interviewer knows you understood it.
- Name the root cause, not just the symptom.
- Give the fix, with a few lines of code if it helps.
- Say how you would stop it happening again: a test, monitoring, or a code review rule.
- Mention the trade-offs, such as what changes when the app runs on several servers.
Easy Scenarios (S1 to S10)
Everyday coding bugs that any developer meets in the first year on a project.
S1. Invoice Total That Doesn't Add Up
Scenario: A billing app adds item prices stored as double. A customer's invoice shows 0.30000000000000004, and the tax total is a cent off.
Question: What is wrong, and how do you fix it?
Show a strong answer
- Cause:
doubleis binary floating point; values such as 0.1 can't be stored exactly, so errors build up. - Fix: use
BigDecimal, created from strings, with explicit rounding:
BigDecimal total = new BigDecimal("0.10")
.add(new BigDecimal("0.20")); // 0.30
BigDecimal withTax = total.multiply(new BigDecimal("1.18"))
.setScale(2, RoundingMode.HALF_UP); // 0.35- Also: compare amounts with
compareTo, notequals(which also compares scale). Some systems store money aslongcents instead. - Prevent: a team rule of "no
doublefor money", plus unit tests with known tricky values.
Revise: BigInteger and BigDecimal in Java.
S2. NullPointerException in the Profile Page
Scenario: Production logs show NullPointerException at user.getAddress().getCity() for some users who never entered an address.
Question: How do you fix it properly, not just with an if?
Show a strong answer
- Read the message: helpful NPE messages (Java 14+) say exactly which part was null, for example
because the return value of "User.getAddress()" is null. - Fix at the right level: decide whether "no address" is valid. If it is, model it:
Optional<Address> getAddress(), and:
String city = user.getAddress()
.map(Address::city)
.orElse("Not provided");- If it isn't valid, validate when the user is created (
Objects.requireNonNull) so bad data never gets in. - Prevent: a unit test with a user who has no address; avoid returning
nullfrom methods.
Revise: Helpful NullPointerException Messages and Optional Class in Java.
S3. Login Works on One Laptop but Not in Production
Scenario: A junior developer wrote if (inputPassword == storedPassword). Sometimes valid logins fail.
Question: What are the bugs here, functional and security?
Show a strong answer
- Functional bug:
==compares references. Strings that came from the database or a request are different objects, so it fails. Useequals. - Security bug: passwords must never be stored or compared in plain text. Store a salted, slow hash (for example PBKDF2 via the JDK, or bcrypt/Argon2) and compare hashes.
- Compare hash bytes in constant time with
MessageDigest.isEqual(a, b)to avoid timing attacks. - Prevent: a code review checklist entry ("no
==on strings"), and static analysis (SonarLint flags it).
Revise: String Comparison in Java and Storing Passwords Safely in Java.
S4. Duplicate Customers in a Report
Scenario: A nightly job collects customers into a HashSet<Customer> to remove duplicates, but the report still lists the same customer twice.
Question: Why, and how do you fix it?
Show a strong answer
- Cause:
Customerdoesn't overrideequalsandhashCode, so the set compares object identity. Two objects with the same data count as different. - Fix: decide what makes two customers the same (for example, the email address) and implement
equalsandhashCodeon exactly those fields. If all fields define identity, arecorddoes it for you. - Watch out: never change those fields while the object is in a set or map.
- Prevent: a unit test that adds two equal customers and expects a set of size 1.
S5. Canceling Orders Crashes the Job
Scenario: A cleanup job loops over List<Order> with a for-each loop and calls orders.remove(o) for canceled orders. It throws ConcurrentModificationException.
Question: Fix it.
Show a strong answer
- Cause: for-each uses a fail-fast iterator; changing the list directly during the loop breaks it.
- Fix:
orders.removeIf(o -> o.status() == Status.CANCELED);- Or build a new list with a stream filter. If many threads share the list, a concurrent collection is needed instead.
- Prevent: prefer
removeIfor streams over manual removal in loops.
S6. Log Analyzer Runs Out of Memory
Scenario: A tool reads a 2 GB server log with Files.readAllLines(path) to count ERROR lines, and dies with OutOfMemoryError: Java heap space.
Question: How do you process the file?
Show a strong answer
- Cause:
readAllLinesloads every line into memory at once. - Fix: stream the file lazily, one line at a time, and close it:
try (Stream<String> lines = Files.lines(path)) {
long errors = lines.filter(l -> l.contains("ERROR")).count();
}- A
BufferedReaderloop works too. Memory use stays small no matter how big the file is. - Prevent: treat input size as unbounded; test with a large file.
Revise: How to Read a File in Java.
S7. Meeting Invites Show the Wrong Time
Scenario: A scheduling app stores meeting times as LocalDateTime. Users in London and Bengaluru see different, wrong times for the same meeting.
Question: What is the right way to store and show times?
Show a strong answer
- Cause:
LocalDateTimehas no time zone, so it isn't a real moment in time. - Fix: store an
Instant(UTC), and convert only for display:
Instant start = meeting.start();
ZonedDateTime forUser = start.atZone(ZoneId.of("Asia/Kolkata"));- Store the organizer's
ZoneIdtoo, for recurring meetings across daylight-saving changes. - Prevent: a rule of "UTC in storage, local time only in the UI"; tests that cross a daylight-saving change.
Revise: ZonedDateTime, ZoneId and Instant.
S8. Database Password in the Source Code
Scenario: The DB URL and password are hard-coded in a Java class, and the app must run in dev, test and production.
Question: How should configuration work?
Show a strong answer
- Read settings from outside the code: environment variables, with a properties file for defaults:
String url = System.getenv()
.getOrDefault("DB_URL", props.getProperty("db.url"));- Never commit secrets to Git; use a secrets manager or the deployment platform's secret store, and rotate the leaked password.
- Prevent: secret scanning in CI, and one config class that validates required settings at startup and fails fast.
Revise: System Class in Java and Reading and Writing Properties Files.
S9. "Too Many Open Files"
Scenario: After a few hours, an import service fails with IOException: Too many open files.
Question: What is the likely cause and fix?
Show a strong answer
- Cause: files, sockets or connections are opened but not closed, especially when exceptions skip the
close()call. - Fix: use try-with-resources for everything that is
AutoCloseable:
try (BufferedReader reader = Files.newBufferedReader(path)) {
// read
}- Check: count open handles (
lsof -p <pid>) to confirm the leak. - Prevent: static analysis rules for unclosed resources, and code review.
Revise: Try-With-Resources in Java.
S10. CSV Export Takes Minutes
Scenario: An export builds a 100,000-row CSV with csv += row + "\n"; in a loop, and it takes minutes.
Question: Why is it slow, and what do you use instead?
Show a strong answer
- Cause: strings are immutable, so each
+=copies everything built so far: O(n²) work. - Fix: use a
StringBuilder, or better, a stream withCollectors.joining, or write directly to a file with aBufferedWriter:
String csv = rows.stream()
.map(Row::toCsvLine)
.collect(Collectors.joining("\n"));- For very large exports, write row by row to the output stream instead of building one big string.
- Prevent: a performance test with realistic data size.
Revise: StringBuilder in Java.
Medium Scenarios (S11 to S20)
Design and data handling: sorting, grouping, APIs, databases and caches.
S11. Product Listing Sort Order
Scenario: An e-commerce page must list products by price (low to high), then rating (high to low), then name (A to Z).
Question: Write the sort.
Show a strong answer
products.sort(Comparator.comparing(Product::price)
.thenComparing(Product::rating, Comparator.reverseOrder())
.thenComparing(Product::name));- Mention
nullhandling (Comparator.nullsLast) and that sorting on large catalogs usually belongs in the database query (ORDER BY) with pagination.
Revise: Comparator Chaining in Java.
S12. Revenue per Customer
Scenario: The finance team wants total order value per customer from a list of Order(customerId, amount), where amount is a BigDecimal.
Question: Write it with streams.
Show a strong answer
Map<String, BigDecimal> totals = orders.stream()
.collect(Collectors.groupingBy(Order::customerId,
Collectors.reducing(BigDecimal.ZERO, Order::amount,
BigDecimal::add)));- Mention why not
summingDouble(money precision), and that for millions of rows a SQLGROUP BYis usually better than loading everything into memory.
S13. Top 10 Words in Support Tickets
Scenario: Product managers want the 10 most common words in this month's support tickets, ignoring case and punctuation.
Question: Implement it.
Show a strong answer
Map<String, Long> freq = tickets.stream()
.flatMap(t -> Arrays.stream(t.toLowerCase().split("\\W+")))
.filter(w -> !w.isBlank())
.collect(Collectors.groupingBy(w -> w, Collectors.counting()));
List<Map.Entry<String, Long>> top10 = freq.entrySet().stream()
.sorted(Map.Entry.<String, Long>comparingByValue().reversed())
.limit(10)
.toList();- Mention filtering stop words ("the", "is"), and that a
PriorityQueueof size 10 avoids sorting every entry.
Revise: Stream flatMap in Java and groupingBy, partitioningBy, toMap and Joining.
S14. Order API That Sometimes Returns Null
Scenario: orderService.findOrders(customerId) returns null when a customer has no orders, and callers keep crashing.
Question: How should this API be designed?
Show a strong answer
- Return an empty list (
List.of()), nevernull, for "no results". Callers can loop safely. - For a single result that may be missing, return
Optional<Order>. - For invalid input (an unknown customer ID), throw a clear exception rather than return empty, so errors aren't hidden.
- Prevent: document the contract, and add tests for the "no orders" case.
Revise: Null Handling Strategies in Java.
S15. Two Customers Buy the Last Item
Scenario: Two users click "Buy" on the last item at the same moment. Both orders succeed, and stock becomes −1.
Question: Fix the race condition.
Show a strong answer
- Cause: a check-then-act race: both threads read
stock == 1, then both decrement. - In the database (the usual real fix): make the update atomic and conditional, then check the row count:
// UPDATE product SET stock = stock - 1 WHERE id = ? AND stock > 0
int updated = ps.executeUpdate();
if (updated == 0) throw new OutOfStockException(productId);- In memory (a single instance): use a CAS loop on an
AtomicInteger:
int current;
do {
current = stock.get();
if (current == 0) return false;
} while (!stock.compareAndSet(current, current - 1));
return true;- Mention optimistic locking with a version column, and that
synchronizeddoesn't help when several app instances run.
Revise: Race Conditions and Thread Safety and Compare-And-Swap (CAS) in Java.
S16. Payment Gateway Is Slow
Scenario: Checkout calls an external payment-status API that sometimes takes 30 seconds, which blocks users.
Question: How do you call it safely?
Show a strong answer
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(2))
.build();
HttpRequest request = HttpRequest.newBuilder(URI.create(statusUrl))
.timeout(Duration.ofSeconds(3))
.GET()
.build();
CompletableFuture<String> status = client
.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.exceptionally(ex -> "PENDING"); // fallback: check again later- Always set timeouts. Retry only safe operations, with exponential backoff and a limit. Use a circuit breaker so a failing gateway isn't hammered. Show users a "processing" state instead of making them wait.
Revise: HttpClient in Java and CompletableFuture in Java.
S17. Sending Emails Slows Down Orders
Scenario: The order API sends a confirmation email in the same request, and email delays make checkout slow.
Question: How do you decouple it?
Show a strong answer
- Send the email asynchronously on a bounded thread pool, so the order completes immediately:
ExecutorService mailPool = new ThreadPoolExecutor(
4, 4, 0L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(1_000),
new ThreadPoolExecutor.CallerRunsPolicy());
mailPool.submit(() -> mailer.sendConfirmation(order));- Bounded queue + rejection policy = no unbounded memory growth. Shut the pool down cleanly on exit.
- Real-world upgrade: in-memory tasks are lost on a crash, so for reliability write an "email to send" record in the same DB transaction as the order (the outbox pattern), or publish to a message queue.
Revise: Types of Thread Pools in Java.
S18. SQL Injection Found in Search
Scenario: A security scan finds that the product search builds SQL as "... WHERE name LIKE '%" + term + "%'".
Question: Fix it.
Show a strong answer
String sql = "SELECT id, name, price FROM product WHERE name LIKE ?";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, "%" + term + "%");
try (ResultSet rs = ps.executeQuery()) { /* map rows */ }
}- Parameters are sent separately from the SQL text, so input can't change the query. Also: limit result sizes, give the DB user the least privileges it needs, and check other queries for the same pattern.
S19. Bulk Import Takes 20 Minutes
Scenario: Importing 100,000 customers inserts one row at a time with auto-commit on, and takes 20 minutes.
Question: Speed it up.
Show a strong answer
conn.setAutoCommit(false);
try (PreparedStatement ps = conn.prepareStatement(
"INSERT INTO customer (id, name) VALUES (?, ?)")) {
int count = 0;
for (Customer c : customers) {
ps.setLong(1, c.id());
ps.setString(2, c.name());
ps.addBatch();
if (++count % 1_000 == 0) {
ps.executeBatch();
conn.commit();
}
}
ps.executeBatch();
conn.commit();
}- Batching cuts network round trips, and committing in chunks avoids one huge transaction. Mention driver settings (MySQL's
rewriteBatchedStatements=true), handling a failed batch, and the database's own bulk-load tools for very large files.
Revise: Batch Processing in JDBC.
S20. Product Cache That Ate the Heap
Scenario: A product cache is a static HashMap that only ever grows. After a week, the service runs out of memory.
Question: Design a better cache.
Show a strong answer
- An unbounded static map is a memory leak. Bound it and evict old entries, for example with an LRU cache:
class LruCache<K, V> extends LinkedHashMap<K, V> {
private final int maxEntries;
LruCache(int maxEntries) {
super(16, 0.75f, true); // access order
this.maxEntries = maxEntries;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > maxEntries;
}
}- It isn't thread-safe: wrap it with
Collections.synchronizedMap, or in production use a proven library such as Caffeine (size limits, expiry, statistics). Also decide how stale data is invalidated when prices change.
Revise: LinkedHashMap in Java and Memory Leaks in Java.
Hard Scenarios (S21 to S30)
Production incidents and system design: memory, deadlocks, CPU, load, latency and payments.
S21. Memory Keeps Growing Until the Service Crashes
Scenario: A service's memory grows slowly and it crashes with OutOfMemoryError: Java heap space every few days. Restarting "fixes" it.
Question: How do you investigate and fix it?
Show a strong answer
- Capture evidence: run with
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/dumps, or take a dump from the live process (jcmd <pid> GC.heap_dump /tmp/heap.hprof). Turn on GC logs. - Analyze: open the dump in Eclipse MAT or VisualVM, look at the dominator tree and the biggest retained sizes, and follow the path to GC roots.
- Typical causes: static collections or caches with no limit; listeners never removed;
ThreadLocals in pools; unclosed resources; sessions kept forever. - Fix and prove it: remove the reference, bound the cache, and confirm with a load test that the heap stays flat after GC.
- Prevent: dashboards for heap after GC; alerts on a growing trend.
Revise: Memory Leaks in Java and Heap Dumps and Thread Dumps.
S22. Every Request Hangs
Scenario: A transfer service suddenly stops responding; CPU is near 0%. Transfers lock two accounts with synchronized blocks.
Question: What happened, and how do you fix it for good?
Show a strong answer
- Diagnose: take a thread dump (
jstack <pid>orjcmd <pid> Thread.print). It reports "Found one Java-level deadlock": thread 1 holds account A and waits for B, while thread 2 holds B and waits for A. - Fix: always lock in a global order, for example by account ID:
Account first = a.id() < b.id() ? a : b;
Account second = first == a ? b : a;
synchronized (first) {
synchronized (second) {
transfer(a, b, amount);
}
}- Alternatives:
ReentrantLock.tryLockwith a timeout and retry, or do the transfer in a single database transaction. - Prevent: a stress test that runs opposite transfers concurrently.
Revise: Deadlock in Java.
S23. CPU Stuck at 100%
Scenario: After a release, one service instance sits at 100% CPU and response times are terrible.
Question: Walk through finding the cause.
Show a strong answer
top -H -p <pid>to find the hottest threads; convert their thread IDs to hex.- Take 3 thread dumps a few seconds apart (
jcmd <pid> Thread.print); match thenid=0x…values and look for the same stack each time. - Check GC logs: constant GC (a nearly full heap) also shows up as high CPU.
- Common causes: an infinite loop, a regex with catastrophic backtracking on user input, a busy-wait loop, or a
HashMapcorrupted by unsynchronized concurrent writes. - Confirm with Java Flight Recorder (
jcmd <pid> JFR.start duration=60s filename=cpu.jfr), fix, and add a regression test.
Revise: Heap Dumps and Thread Dumps and JVM Monitoring and Troubleshooting Tools.
S24. Rate-Limit the Public API
Scenario: A public API must allow at most 100 requests per minute per user; extra requests get HTTP 429.
Question: Implement it in Java for one server instance, and explain what changes with several instances.
Show a strong answer
A token bucket per user allows short bursts and refills smoothly:
class TokenBucket {
private final long capacity;
private final double refillPerNano;
private double tokens;
private long lastRefill = System.nanoTime();
TokenBucket(long capacity, Duration period) {
this.capacity = capacity;
this.tokens = capacity;
this.refillPerNano = (double) capacity / period.toNanos();
}
synchronized boolean tryAcquire() {
long now = System.nanoTime();
double refill = (now - lastRefill) * refillPerNano;
tokens = Math.min(capacity, tokens + refill);
lastRefill = now;
if (tokens < 1) return false;
tokens -= 1;
return true;
}
}
Map<String, TokenBucket> buckets = new ConcurrentHashMap<>();
boolean allowed = buckets
.computeIfAbsent(userId,
id -> new TokenBucket(100, Duration.ofMinutes(1)))
.tryAcquire();computeIfAbsentcreates exactly one bucket per user; locking is per user, not global. Remove idle buckets so the map doesn't grow forever.- Several instances: in-memory limits are per instance, so move the counters to a shared store (for example Redis with atomic scripts), or enforce the limit at the API gateway.
Revise: Concurrent Collections in Java.
S25. 10,000 Concurrent Calls with a 200-Thread Pool
Scenario: A service calls slow downstream APIs. With a fixed pool of 200 platform threads, requests queue up under load. The team wants to handle 10,000 concurrent requests.
Question: How do virtual threads help, and what must you protect?
Show a strong answer
- The work is I/O-bound: threads mostly wait. Virtual threads make waiting cheap, so use one per request:
Semaphore dbPermits = new Semaphore(50); // match the database pool size
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (Request r : requests) {
executor.submit(() -> {
dbPermits.acquire();
try {
return handle(r);
} finally {
dbPermits.release();
}
});
}
}- Protect limited resources: the database still has, say, 50 connections; a
Semaphorestops 10,000 threads from overwhelming it. - Check: no pooling of virtual threads; avoid large
ThreadLocalcaches; look for pinning in native calls (JFR'sjdk.VirtualThreadPinnedevent); CPU-heavy work still needs a normal pool.
Revise: Virtual Thread Best Practices.
S26. Dashboard from Three Slow Services
Scenario: A customer dashboard needs data from profile, orders and loyalty services. Called one after another they take 3+ seconds. The page must respond within 2 seconds, showing partial data if a service is slow.
Question: Design the Java code.
Show a strong answer
Call them in parallel, with a timeout and default for each:
CompletableFuture<Profile> profile = CompletableFuture
.supplyAsync(() -> profileClient.get(id), ioPool)
.completeOnTimeout(Profile.EMPTY, 2, TimeUnit.SECONDS)
.exceptionally(ex -> Profile.EMPTY);
CompletableFuture<List<Order>> orders = CompletableFuture
.supplyAsync(() -> orderClient.recent(id), ioPool)
.completeOnTimeout(List.of(), 2, TimeUnit.SECONDS)
.exceptionally(ex -> List.of());
CompletableFuture<Integer> points = CompletableFuture
.supplyAsync(() -> loyaltyClient.points(id), ioPool)
.completeOnTimeout(0, 2, TimeUnit.SECONDS)
.exceptionally(ex -> 0);
CompletableFuture.allOf(profile, orders, points).join();
return new Dashboard(profile.join(), orders.join(), points.join());- Use a dedicated I/O executor (or virtual threads), not the common pool.
completeOnTimeoutdoesn't cancel the underlying HTTP call, so also set timeouts on the clients. Log which parts fell back. - Modern alternative: structured concurrency (preview in Java 25) cancels the remaining subtasks automatically when the scope ends.
Revise: CompletableFuture in Java and Structured Concurrency in Java.
S27. Latency Spikes from GC Pauses
Scenario: A trading API has p99 latency spikes of 800 ms. GC logs show long pauses.
Question: How do you approach tuning?
Show a strong answer
- Measure: turn on GC logging (
-Xlog:gc*:file=gc.log:time,uptime) and correlate pauses with the spikes. Check heap occupancy after GC, the promotion rate and the allocation rate. - Reduce garbage first: profile allocations (JFR) and remove waste such as boxing in hot loops, large temporary objects, or excessive logging.
- Size the heap correctly: set
-Xmsequal to-Xmxto avoid resizing; make sure the container memory limit leaves headroom. - Choose the collector: tune G1 with
-XX:MaxGCPauseMillis, or switch to ZGC (-XX:+UseZGC) for sub-millisecond pauses at some throughput cost. - Verify with a load test, and change one thing at a time.
Revise: Reading GC Logs in Java and Garbage Collectors in Java.
S28. Orders Jumping to Impossible States
Scenario: Bugs let orders move from SHIPPED back to PENDING, or from CANCELED to DELIVERED.
Question: How do you make illegal state changes impossible to write?
Show a strong answer
Put the rules in one place, the enum itself, and check them on every change:
enum OrderStatus {
PENDING, PAID, SHIPPED, DELIVERED, CANCELED;
boolean canMoveTo(OrderStatus next) {
return switch (this) {
case PENDING -> next == PAID || next == CANCELED;
case PAID -> next == SHIPPED || next == CANCELED;
case SHIPPED -> next == DELIVERED;
case DELIVERED, CANCELED -> false;
};
}
}- The
Orderclass exposesmoveTo(next), which throwsIllegalStateExceptionfor illegal moves; there is no public setter. The exhaustive switch makes the compiler flag any new status that isn't handled. - Also: store a status history for auditing, and guard against two concurrent updates with optimistic locking.
Revise: Enums in Java and Exhaustive Switch with Sealed Types.
S29. Loading Plugins at Runtime
Scenario: A reporting product lets customers drop extra report types as JAR files into a plugins/ folder, without rebuilding the application.
Question: How do you load them in Java?
Show a strong answer
Define a plugin interface, and load implementations with a class loader and ServiceLoader:
URL[] urls = { Path.of("plugins/sales-report.jar").toUri().toURL() };
ClassLoader parent = App.class.getClassLoader();
try (URLClassLoader loader = new URLClassLoader(urls, parent)) {
ServiceLoader.load(ReportPlugin.class, loader)
.forEach(plugin -> plugin.generate(data));
}- Each plugin JAR lists its class in
META-INF/services/<interface name>. - Discuss: a separate class loader per plugin gives isolation and lets a plugin be unloaded (once nothing references its loader); version conflicts between plugins' dependencies; security, since plugins run with full permissions, so only load trusted JARs.
Revise: Class Loaders in Java.
S30. Customers Charged Twice
Scenario: A mobile app retries a payment request after a network timeout. Sometimes the first request had actually succeeded, so the customer is charged twice.
Question: Design idempotent payment processing.
Show a strong answer
- The client sends a unique idempotency key (a UUID) with each logical payment and reuses it on retries.
- The server processes each key once and returns the stored result for repeats.
- Across instances and restarts (required): a table with a unique constraint on the key, written in the same transaction as the payment record. A duplicate insert fails, and you return the saved result.
- Within one instance, also collapse concurrent duplicates:
ConcurrentHashMap<String, CompletableFuture<Receipt>> inFlight =
new ConcurrentHashMap<>();
Receipt pay(String idempotencyKey, PaymentRequest request) {
return inFlight.computeIfAbsent(idempotencyKey,
key -> CompletableFuture.supplyAsync(
() -> gateway.charge(request, key)))
.join();
}- Pass the key on to the payment gateway too (most support one), give stored keys an expiry, and test by sending the same request twice concurrently.
Revise: Transaction Management in JDBC and Concurrent Collections in Java.
What Next
- Practice the shorter questions in 100 Core Java Interview Questions and Answers, filtered by level.
- Rehearse under time pressure with the Java Mock Interview.
- Build something real. Many of these scenarios, such as safe money handling, idempotent requests and bounded caches, make good features for a project you can show in an interview.
Was this tutorial helpful?