Questions
How do you handle concurrency in Java?
The Scenario
You are a backend engineer at a fintech company. You are building a new service that needs to handle a large number of concurrent requests.
You need to find a way to write your code so that it is thread-safe and that it does not have any race conditions.
The Challenge
Explain how you would handle concurrency in Java. What are the different concurrency primitives that you would use, and what are the key benefits of Java’s approach to concurrency?
A junior engineer might not be aware of the different concurrency primitives in Java. They might try to solve the problem by using a `synchronized` block for everything, which would not be very performant.
A senior engineer would have a deep understanding of the different concurrency primitives in Java. They would be able to explain how to use `synchronized` blocks, locks, and atomic variables to write thread-safe code. They would also have a clear plan for how to handle concurrency in a consistent and predictable way.
Step 1: Understand the Key Concurrency Primitives
| Primitive | Description |
|---|---|
synchronized | A keyword that can be used to create a mutually exclusive block of code. |
Lock | An interface that provides a more flexible way to create a mutually exclusive block of code. |
Atomic | A set of classes that provide atomic operations on a single variable. |
volatile | A keyword that can be used to indicate that a variable may be modified by multiple threads. |
Executor | An interface for executing tasks in the background. |
Step 2: Choose the Right Tool for the Job
| Use Case | Recommended Primitive |
|---|---|
| Protecting a small block of code | synchronized |
| Protecting a large block of code | Lock |
| Incrementing a counter | AtomicInteger |
| Sharing a variable between threads | volatile |
| Executing tasks in the background | ExecutorService |
Step 3: Code Examples
Here are some code examples that show how to use some of these primitives:
synchronized:
public class Counter {
private int count;
public synchronized void increment() {
count++;
}
}Lock:
public class Counter {
private int count;
private final Lock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
}AtomicInteger:
public class Counter {
private final AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet();
}
} Practice Question
You are building a high-performance application and need to protect a shared resource from concurrent access. Which of the following would be the most appropriate?