AlgoPlusAlgoPlus
Learn/Databases
Lesson

Two-Phase Locking (2PL)

Each transaction acquires all its locks before releasing any — a growing then shrinking phase — which guarantees a serializable schedule.

10 min read Watch it move Build it

Two-phase locking keeps concurrent transactions safe by making each one lock the data it touches before using it. A shared lock is a read lock — many transactions may hold one on the same item at once. An exclusive lock is a write lock — only one holder, blocking every other read and write of that item. The 'two-phase' rule is what turns locking into a *correctness guarantee*.

The two phases

  1. 1Growing phase — the transaction only *acquires* locks, releasing none.
  2. 2Lock point — the moment it takes its last lock; the boundary between the phases.
  3. 3Shrinking phase — from the lock point on, the transaction only *releases* locks, acquiring none.

Because no transaction gives up a lock while it still intends to take more, conflicting transactions are forced to wait their turn. That discipline is enough to guarantee a conflict-serializable schedule.

T1:  lock-X(A)   read/write A     -- growing
     lock-X(B)                    -- growing (this is the LOCK POINT)
     write B
     unlock(A)                    -- shrinking
     unlock(B)                    -- shrinking

     |---- growing ----|---- shrinking ----|

Basic 2PL still risks deadlock

2PL guarantees serializability but *not* liveness. A deadlock arises when two transactions each wait for a lock the other holds:

T1: lock-X(A)  ...  wants lock-X(B)   -- blocked, T2 holds B
T2: lock-X(B)  ...  wants lock-X(A)   -- blocked, T1 holds A

Each waits for the other  =>  circular wait  =>  DEADLOCK
Both transactions are still in their growing phase
Neither has broken the two-phase rule — they are simply each acquiring a lock the other already holds. The system must detect the cycle (a waits-for graph) and abort one transaction to break it.

Strict 2PL

Strict 2PL strengthens the rule: hold *every* exclusive lock until the transaction commits or aborts, collapsing the shrinking phase to the very end. This prevents cascading aborts — no other transaction can read a value that a not-yet-committed transaction might still roll back. It does not, however, remove the deadlock risk.

Why strict is the common default
Plain 2PL lets a transaction release a lock early, so another transaction could read an uncommitted value and then have to abort if the first rolls back. Strict 2PL avoids that entire class of failures by holding write locks to commit — which is why real systems favour it.
Check yourself
Under basic 2PL, T1 holds A and wants B while T2 holds B and wants A. What has happened?