Java Multi-threaded Circular Printing

This article demonstrates several approaches to implement circular printing among multiple threads in Java. The goal is to have three threads print their respective numbeers (0, 1, 2) in order, cycling through 10 times.

1. Flag Variable + Mutex Lock

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Main {

    private static int sharedFlag = 0;
    private static final Lock lock = new ReentrantLock();

    public static void main(String[] args) {
        new Thread(new Worker(0)).start();
        new Thread(new Worker(1)).start();
        new Thread(new Worker(2)).start();
    }

    private static class Worker implements Runnable {
        private final int id;

        Worker(int id) {
            this.id = id;
        }

        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                lock.lock();
                try {
                    while (id != sharedFlag) {
                        lock.unlock();
                        Thread.yield();
                        lock.lock();
                    }
                    System.out.print(id);
                    sharedFlag = (sharedFlag + 1) % 3;
                } finally {
                    lock.unlock();
                }
            }
        }
    }
}

2. Flag Variable + Mutex Lock + Condition Variable

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Main {

    private static int sharedFlag = 0;
    private static final Lock lock = new ReentrantLock();
    private static final Condition[] conditions = new Condition[3];

    public static void main(String[] args) {
        conditions[0] = lock.newCondition();
        conditions[1] = lock.newCondition();
        conditions[2] = lock.newCondition();

        new Thread(new Worker(0)).start();
        new Thread(new Worker(1)).start();
        new Thread(new Worker(2)).start();
    }

    private static class Worker implements Runnable {
        private final int id;

        Worker(int id) {
            this.id = id;
        }

        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                lock.lock();
                try {
                    if (id != sharedFlag) {
                        conditions[id].await();
                    }
                    System.out.print(id);
                    sharedFlag = (id + 1) % 3;
                    conditions[sharedFlag].signal();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                } finally {
                    lock.unlock();
                }
            }
        }
    }
}

3. Flag Variable + synchornized

public class Main {

    private static int sharedFlag = 0;

    public static void main(String[] args) {
        new Thread(new Worker(0)).start();
        new Thread(new Worker(1)).start();
        new Thread(new Worker(2)).start();
    }

    private static class Worker implements Runnable {
        private final int id;

        Worker(int id) {
            this.id = id;
        }

        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                while (true) {
                    synchronized (Worker.class) {
                        if (id == sharedFlag) {
                            break;
                        }
                    }
                    Thread.yield();
                }
                System.out.print(id);
                sharedFlag = (id + 1) % 3;
            }
        }
    }
}
public class Main {

    private static int sharedFlag = 0;

    public static void main(String[] args) {
        new Thread(new Worker(0)).start();
        new Thread(new Worker(1)).start();
        new Thread(new Worker(2)).start();
    }

    private static class Worker implements Runnable {
        private final int id;

        Worker(int id) {
            this.id = id;
        }

        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                synchronized (Worker.class) {
                    while (id != sharedFlag) {
                        try {
                            Worker.class.wait();
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                        }
                    }
                    System.out.print(id);
                    sharedFlag = (id + 1) % 3;
                    Worker.class.notifyAll();
                }
            }
        }
    }
}

4. Atomic Variable

import java.util.concurrent.atomic.AtomicInteger;

public class Main {

    private static final AtomicInteger sharedFlag = new AtomicInteger(0);

    public static void main(String[] args) {
        new Thread(new Worker(0)).start();
        new Thread(new Worker(1)).start();
        new Thread(new Worker(2)).start();
    }

    private static class Worker implements Runnable {
        private final int id;

        Worker(int id) {
            this.id = id;
        }

        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                while (sharedFlag.get() != id) {
                    Thread.yield();
                }
                System.out.print(id);
                sharedFlag.set((id + 1) % 3);
            }
        }
    }
}

5. Volatile Variable

public class Main {

    private static volatile int sharedFlag = 0;

    public static void main(String[] args) {
        new Thread(new Worker(0)).start();
        new Thread(new Worker(1)).start();
        new Thread(new Worker(2)).start();
    }

    private static class Worker implements Runnable {
        private final int id;

        Worker(int id) {
            this.id = id;
        }

        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                while (sharedFlag != id) {
                    Thread.yield();
                }
                System.out.print(id);
                sharedFlag = (id + 1) % 3;
            }
        }
    }
}

6. Seamphore

import java.util.concurrent.Semaphore;

public class Main {

    private static final Semaphore[] semaphores = new Semaphore[3];

    public static void main(String[] args) {
        semaphores[0] = new Semaphore(1);
        semaphores[1] = new Semaphore(0);
        semaphores[2] = new Semaphore(0);

        new Thread(new Worker(0)).start();
        new Thread(new Worker(1)).start();
        new Thread(new Worker(2)).start();
    }

    private static class Worker implements Runnable {
        private final int id;

        Worker(int id) {
            this.id = id;
        }

        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                try {
                    semaphores[id].acquire();
                    System.out.print(id);
                    semaphores[(id + 1) % 3].release();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }
    }
}

Picture

Tags: java multithreading Concurrency Print in Order

Posted on Fri, 05 Jun 2026 17:01:46 +0000 by MikeyNoedel