Knowledge Guide
HomeConcurrencyConcurrency Problems

Problem 12 Building H2O

Overview

The Building H2O problem is a classic synchronization challenge in concurrent programming. The main challenge is to ensure that two hydrogen threads and one oxygen thread are properly synchronized so that they can form a water molecule, H2O, without violating the given constraints. This scenario highlights the difficulty of coordinating multiple threads to achieve a specific sequence or grouping, making sure that they interleave in a way that leads to the correct result. It mirrors real-world situations where resources (in this case, threads) must be combined in the right proportions to achieve the desired outcome.

Pseudocode

Initialize: semaphore oxygen = 0 semaphore hydrogen = 0 mutex lock Procedure releaseOxygen(): lock.acquire() # Acquire lock to ensure atomic operations oxygen += 1 # Increment oxygen count if oxygen >= 1 and hydrogen >= 2: # Check for available threads to form a water molecule oxygen -= 1 # Decrement oxygen count hydrogen -= 2 # Decrement hydrogen count by 2 barrier.passOxygen() # Allow oxygen thread to pass the barrier barrier.passHydrogen() # Allow two hydrogen threads to pass the barrier barrier.passHydrogen() lock.release() # Release lock Procedure releaseHydrogen(): lock.acquire() # Acquire lock to ensure atomic operations hydrogen += 1 # Increment hydrogen count if oxygen >= 1 and hydrogen >= 2: # Check for available threads to form a water molecule oxygen -= 1 # Decrement oxygen count hydrogen -= 2 # Decrement hydrogen count by 2 barrier.passOxygen() # Allow oxygen thread to pass the barrier barrier.passHydrogen() # Allow two hydrogen threads to pass the barrier barrier.passHydrogen() lock.release() # Release lock

Note:

Algorithm Walkthrough

Algorithm walkthrough or dry run for the given C++ program, demonstrating the synchronization of Hydrogen and Oxygen atoms to create water molecules (H2O).

Initial Setup:

Thread Creation:

Dry Run Steps:

  1. t1 calls releaseHydrogen():

    • Locks the mutex.
    • hydrogenCount is 0, so it increments to 1.
    • Calls generateHydrogen(), printing "Hydrogen (H) generated!".
    • Unlocks the mutex.
  2. t2 calls releaseHydrogen():

    • Locks the mutex.
    • hydrogenCount is 1, so it increments to 2.
    • Calls generateHydrogen(), printing "Hydrogen (H) generated!".
    • Signals t3 because there are now 2 Hydrogen atoms.
    • Unlocks the mutex.
  3. t3 calls releaseOxygen():

    • Locks the mutex.
    • Finds 2 Hydrogen atoms available (from steps 1 and 2).
    • Resets hydrogenCount to 0.
    • Calls generateOxygen(), printing "Oxygen (O) generated!".
    • Prints "H2O is created!".
    • Broadcasts signal to Hydrogen condition variable (wakes up any waiting Hydrogen threads).
    • Unlocks the mutex.
  4. Steps 1 to 3 repeat until all threads finish their iterations.

Considerations

By the end of the execution, you would see "Hydrogen (H) generated!", "Oxygen (O) generated!", and "H2O is created!" printed 20, 10, and 10 times, respectively, representing the formation of 10 water molecules.

Flow Chart
Flow Chart
java
import java.util.concurrent.Semaphore;

public class Solution {

  // Semaphore to control the release of Hydrogen atoms
  private final Semaphore hSemaphore;

  // Semaphore to control the release of Oxygen atoms
  private final Semaphore oSemaphore;

  // Counter to keep track of the number of Hydrogen atoms
  private int hCount;

  public Solution() {
    // Initialize semaphores, 2 permits for Hydrogen and 0 permits for Oxygen
    // 2 permits for Hydrogen because we need two Hydrogens per H2O molecule
    // 0 permits for Oxygen because we need to wait for two Hydrogens before releasing Oxygen
    hSemaphore = new Semaphore(2);
    oSemaphore = new Semaphore(0);

    // Initialize the Hydrogen counter
    hCount = 0;
  }

  // Method to release a Hydrogen atom
  public void hydrogen(Runnable releaseHydrogen) throws InterruptedException {
    // Acquire a permit to release Hydrogen
    hSemaphore.acquire();

    synchronized (this) {
      // Release Hydrogen atom
      releaseHydrogen.run();

      // Increment the Hydrogen counter
      hCount++;

      // Check if there are two Hydrogen atoms
      if (hCount == 2) {
        // If yes, release an Oxygen atom
        oSemaphore.release();
      }
    }

    // Release the permit for the next Hydrogen
    hSemaphore.release();
  }

  // Method to release an Oxygen atom
  public void oxygen(Runnable releaseOxygen) throws InterruptedException {
    // Acquire a permit to release Oxygen
    oSemaphore.acquire();

    synchronized (this) {
      // Release Oxygen atom
      releaseOxygen.run();

      // Print the creation of a water molecule
      System.out.println("H2O is generated!");

      // Reset the Hydrogen counter for the next water molecule
      hCount = 0;

      // Release permits for the next two Hydrogen atoms
      hSemaphore.release(2);
    }
  }

  public static void main(String[] args) {
    Solution h2o = new Solution();

    // Lambda expression to define the release of Hydrogen
    Runnable releaseHydrogen = () -> System.out.print("H ");

    // Lambda expression to define the release of Oxygen
    Runnable releaseOxygen = () -> System.out.print("O ");

    // Start threads to simulate the release of atoms
    for (int i = 0; i < 5; i++) {
      new Thread(() -> {
        try {
          h2o.hydrogen(releaseHydrogen);
        } catch (InterruptedException e) {
          Thread.currentThread().interrupt();
        }
      }).start();

      new Thread(() -> {
        try {
          h2o.hydrogen(releaseHydrogen);
        } catch (InterruptedException e) {
          Thread.currentThread().interrupt();
        }
      }).start();

      new Thread(() -> {
        try {
          h2o.oxygen(releaseOxygen);
        } catch (InterruptedException e) {
          Thread.currentThread().interrupt();
        }
      }).start();
    }
  }
}
🤖 Don't fully get this? Learn it with Claude

Stuck on Problem 12 Building H2O? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🪜 Hint ladder (no spoilers)

Progressively stronger hints — you still solve it.

I'm working on the problem **Problem 12 Building H2O** (Concurrency). Give me a HINT LADDER: start with the tiniest nudge, then wait. Only reveal the next, stronger hint when I ask. Do NOT show the full solution unless I type 'show solution'. Keep me doing the thinking. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🎨 Explain the approach visually

See the technique, not just code.

Explain the optimal approach to **Problem 12 Building H2O** with a VISUAL walkthrough: trace it on a small concrete example using ASCII art / a step-by-step diagram, narrate what changes each step, then give time & space complexity with a one-line derivation. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🔍 Review my solution

Catch bugs, edge cases, sub-optimality.

I'll paste my solution to **Problem 12 Building H2O**. Review it for correctness, missed edge cases, and time/space complexity, then coach me toward the optimal — don't just rewrite it. Ask me to paste my code now. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🔁 Drill the pattern

Lock in recognition with look-alikes.

Give me 2 problems that use the SAME underlying pattern as **Problem 12 Building H2O**. For each, let me attempt first, then review my answer and name the trigger signal that reveals the pattern. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes