It is not possible that two (or more) threads acquire lock on the same time. There are few types of synchronization methods for instance:
Active waiting - spin lock
1. while ( xchg(lock, 1) == 1); - entry protocole
XCHG is an example of atomic operation (exists on x86 architecture) which first sets new value for a "lock" variable and then returns old value. Atomic means that it can't be interrupt - in above example between setting new value and returning old. Atomic - deterministic result no matter what.
2. Your code
3. lock = 0; - exit protocol
When lock is equal to 0 another thread can enter to critical section - while loop ends.
Thread suspending - for example counting semaphore
There exists two atomic operation
.Signal() and we have integer variable lets call it
if (currentValue > 0) currentValue -= 1;
else suspend current thread;
If there exists thread suspended by semaphore wake up one of them
Else currentValue += 1;
Now solving critical section problem is really easy:
do some operations - critical section
Usually your programming thread API should give you ability to specify maximal concurrent threads in semaphore critical section. Obviously there are more types of synchronization in multithreaded systems (mutex, monitors, binary semaphore etc.) but they base on above ideas. One could argue that methods which use thread suspending should be preferred to active waiting (so cpu is not wasted) - it's not always the truth. When thread is being suspended - expensive operation called context switch takes it place. However it's reasonable when waiting time is short ( number of threads ~ number of cores ).