Deadlock means threads are stuck and not moving.

Livelock is trickier: threads are moving, reacting, and retrying, but useful work still does not complete.

The system looks active while progress remains near zero.


Problem Statement

Suppose two workers try to avoid contention politely.

Each worker checks whether the other is active, backs off, and retries. If both keep doing that in sync, they can spend all their time yielding to each other.

That is livelock.


Runnable Example

import java.util.concurrent.TimeUnit;

public class LivelockDemo {

    public static void main(String[] args) throws Exception {
        SharedPath path = new SharedPath();

        Thread t1 = new Thread(() -> path.pass("worker-1"), "worker-1");
        Thread t2 = new Thread(() -> path.pass("worker-2"), "worker-2");

        t1.start();
        t2.start();

        TimeUnit.SECONDS.sleep(2);
        System.out.println("t1 state = " + t1.getState());
        System.out.println("t2 state = " + t2.getState());
    }

    static final class SharedPath {
        private volatile String preferred = "worker-1";

        void pass(String worker) {
            int attempts = 0;
            while (attempts < 20) {
                if (preferred.equals(worker)) {
                    preferred = other(worker);
                    attempts++;
                    sleep(50);
                    continue;
                }

                System.out.println(worker + " passed after attempts = " + attempts);
                return;
            }

            System.out.println(worker + " gave up after livelock-like retries");
        }

        String other(String worker) {
            return "worker-1".equals(worker) ? "worker-2" : "worker-1";
        }
    }

    static void sleep(long millis) {
        try {
            TimeUnit.MILLISECONDS.sleep(millis);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException(e);
        }
    }
}

This is a teaching example, but the pattern is realistic: repeated conflict detection plus symmetric retry logic can destroy progress.


Deadlock vs Livelock

Deadlock:

  • threads block waiting
  • state is frozen
  • no one releases what others need

Livelock:

  • threads keep changing state
  • they keep reacting to each other
  • useful work still does not complete

In operations terms:

  • deadlock looks stuck
  • livelock looks busy but ineffective

Production-Style Scenarios

Livelock appears in systems like:

  • aggressive retry loops with immediate conflict detection
  • two services repeatedly cancelling and recreating the same work
  • lock acquisition code that releases and retries too eagerly
  • contention-avoidance logic that causes synchronized backoff patterns

A classic symptom is high activity with poor throughput:

  • many retries
  • many log lines
  • little business progress

Why It Happens

Livelock often comes from well-intentioned code:

  • detect contention
  • back off
  • retry immediately

If all actors follow the same policy at roughly the same time, they can keep colliding forever.

This is common when systems lack jitter, fairness, or ownership.


Better Design

Useful mitigation techniques include:

  • random backoff instead of synchronized retry
  • bounded retries with fallback
  • one owner per resource or queue partition
  • fairer coordination primitives when appropriate
  • conflict resolution rules that break symmetry

The general goal is to stop all participants from making the same decision at the same time.


Realistic Example

Two schedulers try to claim the same job row:

  • scheduler A checks lock, sees contention, releases and retries
  • scheduler B does the same
  • both continue contending on every retry cycle

Without jitter or lease ownership rules, the system can burn compute while job completion stays poor.


Testing and Review Notes

Livelock deserves tests that look for lack of useful progress, not only for blocked states. That is a subtle but important distinction. A livelocked system may show active threads, increasing retry counters, and lots of log noise while still completing almost no real work.

In review, ask whether the conflict-resolution strategy breaks symmetry. If all contenders react the same way at the same time, livelock risk rises sharply. Add jitter, bounded retries, or an ownership rule so that “be polite and retry” does not become a permanent traffic dance.

Key Takeaways

  • Livelock means the system is active but not making useful progress.
  • It often comes from symmetric retry or conflict-avoidance logic.
  • Deadlock blocks; livelock spins and reacts.
  • Jitter, bounded retries, and clearer ownership rules are common fixes.

Next post: Starvation in Java Concurrency

Comments