Backend Development 7 min read

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.

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

login 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.