Something changed from Java8 to Java9 behind the scenes in Thread Scheduler. I'm trying to narrow down the change in below program.
Below program spawns 3 threads which run parallely and synchronously passing the monitor lock properly, printing
Aa0Bb1Cc2Dd3.......Zz25
Current Code is already working fine in all Java versions and i'm not looking for any optimizations.
I used Object.notifyAll() before passing the lock using Object.wait() (that may not be correct all the time but in this situation it didn't make a difference in java 1.8). That's why there's two versions of this code version 1 and version 2.
version 1 runs fine in all java versions(Java8 and prior, Java9 and later). But not version 2. When you comment version 1 and un-comment version 2 for examlpe like this
//obj.wait();//version 1
obj.notifyAll();obj.wait();//version 2
It runs exactly the same in Java8 where as in Java9 and later JDKs it doesn't. It fails to grab the lock or it grabs the lock but condition has already been flipped and it's no thread's turn.
(for example in let's say numb thread finished its job and now only thread which can grab the lock and proceed is ThreadCapital but somehow isCapital has been turned false - this is just a speculation can't prove this or not sure this is even happening)
I've little experience working with threads so i'm sure i didn't exploit the lock on monitor or even if i had it should reflect same in all JDKs. unless something changed in Java9 and later versions. Did anything change internally in thread scheduler or something?
package Multithreading_misc;
public class App {
public static void main(String[] args) throws InterruptedException {
SimpleObject obj = new SimpleObject();
ThreadAlphaCapital alpha = new ThreadAlphaCapital(obj);
ThreadAlphaSmall small = new ThreadAlphaSmall(obj);
ThreadNum num = new ThreadNum(obj);
Thread tAlpha = new Thread(alpha);
Thread tSmall = new Thread(small);
Thread tNum = new Thread(num);
tAlpha.start();
tSmall.start();
tNum.start();
}
}
class ThreadAlphaCapital implements Runnable{
char c = 'A';
SimpleObject obj;
public ThreadAlphaCapital(SimpleObject obj){
this.obj = obj;
}
@Override
public void run() {
try {
synchronized (obj) {
while(c < 'Z')
{
if(!obj.isCapitalsTurn || obj.isNumsTurn)
{
obj.wait();//version 1
//obj.notifyAll();obj.wait();//version 2
}
else
{
Thread.sleep(500);
System.out.print(c++);
obj.isCapitalsTurn = !obj.isCapitalsTurn;
obj.notifyAll();//version 1
//obj.notifyAll();obj.wait();//version 2
}
}
obj.notifyAll();
}
}
catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
class ThreadAlphaSmall implements Runnable{
char c = 'a';
SimpleObject obj;
public ThreadAlphaSmall(SimpleObject obj){
this.obj = obj;
}
@Override
public void run() {
try {
synchronized (obj) {
while(c < 'z')
{
if(obj.isCapitalsTurn || obj.isNumsTurn)
{
obj.wait();//version 1
//obj.notifyAll();obj.wait();//version 2
}
else
{
Thread.sleep(500);
System.out.print(c++);
obj.isCapitalsTurn = !obj.isCapitalsTurn;
obj.isNumsTurn = !obj.isNumsTurn;
obj.notifyAll();//version 1
//obj.notifyAll();obj.wait();//version 2
}
}
obj.notifyAll();
}
}
catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
class ThreadNum implements Runnable{
int i = 0;
SimpleObject obj;
public ThreadNum(SimpleObject obj){
this.obj = obj;
}
@Override
public void run() {
try {
synchronized (obj) {
while(i < 26)
{
if(!obj.isNumsTurn)
{
obj.wait();//version 1
//obj.notifyAll();obj.wait();//version 2
}
else
{
Thread.sleep(500);
System.out.print(i++);
obj.isNumsTurn = !obj.isNumsTurn;
obj.notifyAll();//version 1
//obj.notifyAll();obj.wait();//version 2
}
}
obj.notifyAll();
}
}
catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
class SimpleObject{
public boolean isNumsTurn = false;
public boolean isCapitalsTurn = true;
}
Few Notes:
This is being run from UnNamed module when run from Java9 and later versions
I'm not saying this is only happening with 3 threads, just giving an example. btw it (overnotifying) runs fine for two threads for all java versions