98 lines
3.3 KiB
Markdown
98 lines
3.3 KiB
Markdown
## Question 1
|
|
An **atomic variable** is a special type of variable that performs operations
|
|
|
|
The purpose of using them is to prevent **race conditions**
|
|
|
|
- **Atomic variables**: thread-safe for simple operations (like increment, update).
|
|
|
|
- **Ordinary variables**: can cause race conditions if multiple threads change them together without protection.
|
|
|
|
## Question 2
|
|
|
|
Examples of classes in java.util.concurrent.atomic:
|
|
|
|
1. AtomicInteger
|
|
2. AtomicLong
|
|
3. AtomicBoolean
|
|
4. AtomicReference
|
|
|
|
AtomicInteger:
|
|
Used as a thread-safe counter. For example, in a multithreaded server, multiple threads can increase the number of requests safely without using synchronized.
|
|
|
|
## Question 3
|
|
|
|
Atomic variables: It's better for simple operations on a single value (increment, update, compare-and-set).
|
|
They are usually faster and lighter because they avoid blocking threads.
|
|
|
|
Locks: Used when multiple operations must happen together. Threads wait until the lock is released.
|
|
|
|
|
|
Use a lock when:
|
|
|
|
- Multiple variables must stay consistent together
|
|
|
|
- A complex sequence of actions must be protected
|
|
|
|
- You need critical sections with more control
|
|
|
|
|
|
Use an atomic variable when:
|
|
|
|
- Only one variable is shared
|
|
|
|
- Operations are simple (counter)
|
|
|
|
- High performance and low overhead are important
|
|
|
|
|
|
## Question 4
|
|
|
|
A program can be free of race conditions but still be slow under high contention because correctness does not mean good performance.
|
|
Threads may work safely but spend time waiting instead of doing useful work.
|
|
|
|
Three factors that limit scalability:
|
|
|
|
1. Lock Contention
|
|
Many threads try to access the same lock. Only one can continue, so others wait, reducing parallelism.
|
|
|
|
|
|
2. Context Switching
|
|
The CPU constantly switches between threads. Frequent switching adds overhead and wastes time.
|
|
|
|
|
|
3. Synchronization Overhead
|
|
Operations like locks, atomic updates, and coordination between threads require extra CPU work and memory synchronization.
|
|
|
|
|
|
## Question 5
|
|
|
|
Adding more threads does not always improve performance because after a certain point, threads start spending more time coordinating and competing than doing actual work.
|
|
|
|
1. When there are too many threads, the CPU frequently switches between them.
|
|
Saving and restoring thread states creates overhead and reduces efficiency.
|
|
|
|
|
|
2. Multiple threads may compete for the same resources (locks, memory, shared data). Some threads must wait, so parallel execution decreases.
|
|
|
|
|
|
3. Each CPU core has its own cache. When several threads modify shared data, caches must stay synchronized,
|
|
creating extra communication and slowing execution.
|
|
|
|
|
|
4. Locks, atomic operations, and coordination mechanisms require additional processing time to maintain correctness.
|
|
|
|
|
|
## Question 6
|
|
|
|
Deadlocks often appear in production but not during testing. During testing, threads may run in a favorable order and
|
|
never reach the lock sequence that causes deadlock. In production, different timing, higher load, and more threads can create the exact conditions needed.
|
|
|
|
Two strategies to expose deadlocks during testing:
|
|
|
|
1. Stress / Load Testing
|
|
Run the program with many threads, repeated executions, and high concurrency to increase the chance of problems.
|
|
|
|
|
|
2. Introduce Scheduling Variations
|
|
Add random delays, sleeps, or use concurrency testing tools to force different execution orders and increase the probability of triggering deadlocks.
|