Printing ABC in Order with Multiple Threads Using Semaphore in Java

This article explains how to create three Java threads that print the characters A, B, and C in strict order, repeat the sequence twenty times, and demonstrates three implementations—unordered printing, ordered printing with Semaphore, and a looped version—complete with full source code examples.

Full-Stack Internet Architecture
Full-Stack Internet Architecture
Full-Stack Internet Architecture
Printing ABC in Order with Multiple Threads Using Semaphore in Java

In many large‑company interviews a common multithreading question asks candidates to print the characters A, B, and C in order using three separate threads, and to repeat the sequence multiple times. The article first describes the problem and why naïve thread start‑up yields nondeterministic output.

Step 1: Unordered printing – simply start three threads, each printing a single character. The output order is random. The following code shows this basic approach:

package com.sample.interview.multithread;
// 三个线程打印字符ABC,但顺序不确定
public class PrintRandomly {
    public static void main(String[] args) {
        Thread threadA = new Thread(() -> System.out.print("A"));
        Thread threadB = new Thread(() -> System.out.print("B"));
        Thread threadC = new Thread(() -> System.out.print("C"));
        threadA.start();
        threadB.start();
        threadC.start();
    }
}

Step 2: Ordered printing using Semaphore – to enforce the order A → B → C, Java's java.util.concurrent.Semaphore is used as a permit‑based lock. Three semaphores are created with zero initial permits; each thread acquires its own semaphore and releases the next one after printing. The main thread releases the first permit to start the chain.

package com.sample.interview.multithread;
import java.util.concurrent.Semaphore;
// 多线程顺序打印出ABC
public class PrintSequentially {
    public static void main(String[] args) {
        Semaphore semaphoreA = new Semaphore(0);
        Semaphore semaphoreB = new Semaphore(0);
        Semaphore semaphoreC = new Semaphore(0);

        Thread threadA = new Thread(() -> {
            try { semaphoreA.acquire(); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.print("A");
            semaphoreB.release();
        });
        Thread threadB = new Thread(() -> {
            try { semaphoreB.acquire(); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.print("B");
            semaphoreC.release();
        });
        Thread threadC = new Thread(() -> {
            try { semaphoreC.acquire(); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.print("C");
            semaphoreA.release();
        });
        semaphoreA.release(); // start with A
        threadC.start();
        threadB.start();
        threadA.start();
    }
}

Step 3: Repeating the ordered sequence 20 times – by initializing the first semaphore with one permit and looping inside each thread, the same semaphore‑based coordination prints "ABC" twenty consecutive times (resulting in "ABCABC…" 20 times). A helper PrintChar class implements Runnable and performs the acquire‑print‑release cycle inside a for loop.

package com.sample.interview.multithread;
import java.util.concurrent.Semaphore;
// 循环打印ABC20次
public class PrintSequentiallyLoop {
    public static void main(String[] args) {
        Semaphore semaphoreA = new Semaphore(1);
        Semaphore semaphoreB = new Semaphore(0);
        Semaphore semaphoreC = new Semaphore(0);

        PrintChar printCharA = new PrintChar(semaphoreA, semaphoreB, "A");
        PrintChar printCharB = new PrintChar(semaphoreB, semaphoreC, "B");
        PrintChar printCharC = new PrintChar(semaphoreC, semaphoreA, "C");

        new Thread(printCharA).start();
        new Thread(printCharB).start();
        new Thread(printCharC).start();
    }

    static class PrintChar implements Runnable {
        Semaphore cur;
        Semaphore next;
        private String str;
        private int times = 20;
        public PrintChar(Semaphore cur, Semaphore next, String str) {
            this.cur = cur;
            this.next = next;
            this.str = str;
        }
        @Override
        public void run() {
            for (int i = 0; i < times; i++) {
                try { cur.acquire(); } catch (InterruptedException e) { e.printStackTrace(); }
                System.out.print(str);
                next.release();
            }
        }
    }
}

The article concludes that using Semaphore provides a simple yet powerful way to control thread execution order for this classic interview problem, and the same pattern can be adapted to other concurrency challenges.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

javaconcurrencymultithreadingsemaphoreThread Coordination
Full-Stack Internet Architecture
Written by

Full-Stack Internet Architecture

Introducing full-stack Internet architecture technologies centered on Java

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.