# Answers ## Q1 Atomic variable is a type of variable that combines all three process of: read, write and update into a single operation. as an example when we write `i++` it may seem like a single operation but the compiler needs to read `i`(read), add 1 to it (update) and change `i` to new value (write) since all these operations are combined into one operation race conditions doesn't occur unlike normal variables. ## Q2 `AtomicInteeger`, `AtomicBoolean`.`AtomicLong` & `AtomicReference` #### `AtomicInteeger` Use case used as counters that are shared across threads #### `AtomicBoolean` Use case used as flags that are used across threads (like `running` flag) #### `AtomicLong` Use case in general anything that needs long operations for multiple threads such as a bank account balance or an id counter #### `AtomicReference` Use case it's used whenever you need atomic operations on any object ## Q3 locks are used to lock a full block of code, this prevents race conditions as a thread locks the section it's working on when it reaches it therefore we should use locks when we wan't to prevent race conditions for a block of code and use atomic variables on more basic things ## Q4 this situation may occur because if there are too many threads trying to access a shared resource performance drops because workers sleep more than they do work #### Usage of locks locks make a block of code completely unaccessible for every thread except one this would cause those other threads to sleep meaning even if you have 100 threads only one of them can work at a time. #### Usage of atomic variables when we use atomic variables and atomic operations, at a curtain point many threads my try to change one variable only one can successfully access the variable, meaning the rest would fail this failure is just waste of cpu power. ## Q5 majority of mainstream cpus has something around 8 to 16 cores right now this means one thing logically we can't run 100 threads at the same time in parallel this causes something known as context switching: cpu cores need to switch between threads and this continuous switching would make the program slow cpu cores have their own distinct cache, when a core changes a synced variable all the cpu cores change their cache value of that variable when we have too many threads working on a synced variable, cpu cores constantly follow the changes in order to keep their cache synced this would cause cpu cores to spend more time synchronizing the cache rather than processing the threads this situation is known cache coherence synchronization overhead. ## Q6 Deadlocks are rare because you can't determine threads execution order, OS determines this by itself to increase likelihood of deadlocks to happen a developer can start too many threads and delay the threads