Here is the one of the solution to solve the given problem. It uses wait to wait the current thread after printing one letter and notify the other thread to print its letter, and same cycle repeats. The concept of Thread wait and notify is very well explained here.
Short description:
To wait or notify any thread, it (invoking thread) must acquire the lock on any common object. In below example, each thread is acquiring the lock on this (self) (via synchronized run method) to wait and on opponent to notify it (the another thread).
public class App implements Runnable {
char c;
App opponent;
boolean go;
public App(char c, boolean go) {
this.c = c;
this.go = go;
}
public void setOpponent(App opponent) {
this.opponent = opponent;
}
public static void main(String[] args) {
App a = new App('a', true);
App b = new App('A', false);
Thread t1 = new Thread(a);
Thread t2 = new Thread(b);
a.setOpponent(b);
b.setOpponent(a);
t1.start();
t2.start();
}
@Override
public synchronized void run() {
for (char i = 0; i < 26; i++) {
try {
if (go) {
System.out.println(c++);
this.wait();
synchronized (opponent) {
opponent.notify();
}
} else {
System.out.println(c++);
synchronized (opponent) {
opponent.notify();
}
this.wait();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
Here is the more simplified and understandable version of above code. In this code, both the threads are acquiring lock on same lock object, printing the letter, going in WAIT state (so release the lock) and giving turn to other BLOCKED thread waiting for the lock on lock. The other thread then acquires the lock on lock, prints the letter, notifies the previous thread which was waiting on lock object, and goes into WAIT condition, thus releasing the lock.
public class App2 implements Runnable {
char c;
Object lock;
boolean go;
public App2(char c, boolean go) {
this.c = c;
this.go = go;
}
public void setLock(Object lock) {
this.lock = lock;
}
public static void main(String[] args) {
App2 a = new App2('a', true);
App2 b = new App2('A', false);
Thread t1 = new Thread(a);
Thread t2 = new Thread(b);
Object lock = new Object();
a.setLock(lock);
b.setLock(lock);
t1.start();
t2.start();
}
@Override
public void run() {
for (char i = 0; i < 26; i++) {
synchronized (lock) {
try {
if (go) {
System.out.println(c++);
lock.wait();
lock.notify();
} else {
System.out.println(c++);
lock.notify();
lock.wait();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
UPDATE:
Above two approaches will work in most of the cases, but will not guarantee that t1 will be executed before t2 every time. Though t1 is started before t2, but it does not mean that thread scheduler will always pick t1 to execute before t2. To be sure that t1 will execute before t2 in every case, we need to ensure that t2 gets started once t1 is in RUNNABLE state (i.e. running). Below is the one of the way of how we can achieve it:
public class App3 implements Runnable {
char c;
App3 opponent;
boolean go;
boolean createOpponent = false;
public App3(char c, boolean go) {
this.c = c;
this.go = go;
}
public void setOpponent(App3 opponent) {
this.opponent = opponent;
}
public void setCreateOpponent(boolean createOpponent) {
this.createOpponent = createOpponent;
}
public static void main(String[] args) {
App3 a = new App3('a', true);
App3 b = new App3('A', false);
Thread t1 = new Thread(a);
a.setOpponent(b);
a.setCreateOpponent(true);
b.setOpponent(a);
t1.start();
}
@Override
public synchronized void run() {
if (createOpponent) {
setCreateOpponent(false);
new Thread(opponent).start();
}
for (char i = 0; i < 26; i++) {
try {
if (go) {
System.out.println(c++);
this.wait();
synchronized (opponent) {
opponent.notify();
}
} else {
System.out.println(c++);
synchronized (opponent) {
opponent.notify();
}
this.wait();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}