Thread Pool Isolation
Giving each downstream dependency its own pool of threads or permits, so one slow dependency cannot consume the capacity needed to serve everything else.
The most valuable bulkhead in practice, and the one most often missing. Without it, a service with one shared pool and five dependencies loses all capacity when any one of them slows down: threads accumulate on the slow call, the pool exhausts, and requests that never touch that dependency start failing too.
Two implementations with different costs. Separate thread pools give true isolation, including against a dependency that blocks uninterruptibly, at the price of extra threads and a context switch per call. Semaphore isolation simply caps concurrent calls per dependency using counters — much cheaper, and it does not protect against a call that blocks the calling thread forever, since there is no separate thread to abandon.
Sizing each pool is the same Little's Law calculation as any other: expected throughput to that dependency times its expected latency, plus headroom.
The related decision is what happens when a pool is exhausted: rejecting immediately with a fallback is the point of the pattern, and queueing behind an exhausted pool quietly undoes it.