Hammer Keyboard Hammer Keyboard - 3 months ago 13
Java Question

How to write below multithreaded program best way

I am new to multithreading, and get to know about the functionality of wait, notify and notifyAll. I want three threads to execute one after another and print alphabets from A to Z.
I have tried below code and it seems working also, but I doubt if this is the best possible way to tackle the problem. Is there any other way, I can make it more simple and better ? It seems some portion of my code is repeating.

package demo.threading;

class Flags {

boolean flagA = true;
boolean flagB = false;
boolean flagC = false;

}

class Container {

Flags flags = new Flags();
int charVal = (int) 'A';

void producer1() {

try {
while (charVal <= (int) 'Z') {
synchronized (this) {
if (!flags.flagA)
wait();
else {
System.out.println(Thread.currentThread().getName() + " Produced : " + (char) charVal);
flags.flagA = false;
flags.flagB = true;
charVal++;
notifyAll();
Thread.sleep(1000);
}
}
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}

}

void producer2() {

try {
while (charVal <= (int) 'Z') {
synchronized (this) {
if (!flags.flagB)
wait();
else {
System.out.println(Thread.currentThread().getName() + " Produced : " + (char) charVal);
flags.flagB = false;
flags.flagC = true;
charVal++;
notifyAll();
Thread.sleep(1000);
}
}
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}

void producer3() {

try {
while (charVal <= (int) 'Z') {
synchronized (this) {
if (!flags.flagC)
wait();
else {
System.out.println(Thread.currentThread().getName() + " Produced : " + (char) charVal);
flags.flagC = false;
flags.flagA = true;
charVal++;
notifyAll();
Thread.sleep(1000);
}
}
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}

public class Main {
public static void main(String[] args) {

Container container = new Container();

Thread t1 = new Thread(() -> container.producer1(), "Thread 1");
Thread t2 = new Thread(() -> container.producer2(), "Thread 2");
Thread t3 = new Thread(() -> container.producer3(), "Thread 3");

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

}
}


Output should be :

Thread 1 Produced : A
Thread 2 Produced : B
Thread 3 Produced : C
Thread 1 Produced : D
Thread 2 Produced : E
Thread 3 Produced : F

Answer

As pointed out before, if you want to do this "one after another", you actually don't need multiple threads. However, you can achieve this by using a Semaphore:

int numberOfThreads = 3;
Semaphore semaphore = new Semaphore(1);

for (int i = 1; i <= numberOfThreads; i++) {
    new Thread(() -> {
        try {
            semaphore.acquire();
            for (char c : "ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray()) {
                System.out.println(Thread.currentThread().getName() 
                        + " produced: " + c + ".");
            }
        } catch (InterruptedException e) {
            // NOP
        } finally {
            semaphore.release();
        }
    }, "Thread " + i).start();
}

I recommend exploring java.util.concurrent which is available since Java 5. It's a great help to keep your concurrent code concise and simple compared with Java's low-level concurrency primitives such as wait and notify. If you're really interested in that topic, Brian Goetz's "Java Concurrency in Practice" is a must-read.

EDIT:

public class ConcurrentAlphabet {

    private Thread current;

    public static void main(String[] args) {
        new ConcurrentAlphabet().print(3,
                "ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray());
    }

    public void print(int numberOfThreads, char[] alphabet) {
        Thread[] threads = new Thread[numberOfThreads];

        for (int i = 1; i <= numberOfThreads; i++) {
            int offset = i - 1;
            threads[offset] = new Thread(() -> {
                Thread me = Thread.currentThread();
                String name = me.getName();
                Thread next = threads[(offset + 1) % numberOfThreads];

                for (int index = offset; index < alphabet.length; index += numberOfThreads) {
                    synchronized (this) {
                        while (!current.equals(me)) {
                            try {
                                wait();
                            } catch (InterruptedException e) { /* NOP */ }
                        }

                        System.out.println(
                                name + " produced: " + alphabet[index] + ".");
                        current = next;
                        notifyAll();
                    }
                }
            }, "Thread " + i);
        }

        current = threads[0];

        for (Thread t : threads) {
            t.start();
        }
    }

}