carlisle carlisle - 16 days ago 4
Java Question

Why I can't judge the variable in the main thread?

I want to write two Thread to add a number and suscribe a number. And the main Thread to judge wheather the two number is equal. For example, one number is 0 and the other number is 10. And when they are also 5, the main Thread should judge it and print "They meet!".
But now the main Thread can't not judge successfully.

public class Number implements Runnable {
public static int numup = 0;
public static int numdown = 10;

public Number() {
}

public static void main(String args[]) {
Number number = new Number();
Thread T1 = new Thread(number, "up");
Thread T2 = new Thread(number, "down");
T1.start();
T2.start();

while (true) {
if (numup == 5 && numdown == 5) {
System.out.println("Meet!");
System.exit(0);
}
}

}

public void run() {
while (true) {
if (Thread.currentThread().getName().equals("up")) {

numup++;
System.out.println(numup);

} else if (Thread.currentThread().getName().equals("down")) {

numdown--;
System.out.println(numdown);
}

try {
Thread.sleep(1000);
} catch (Exception e) {
System.out.println("wake!");
}
}

}
}


The failed result

However, when I make the main Thread to sleep a few millis, it can judge!

public class Number implements Runnable {
public static int numup = 0;
public static int numdown = 10;

public Number() {
}

public static void main(String args[]) {
Number number = new Number();
Thread T1 = new Thread(number, "up");
Thread T2 = new Thread(number, "down");
T1.start();
T2.start();

while (true) {

try {
Thread.sleep(10);
} catch (Exception e) {
System.out.println(Thread.currentThread().getName() + "was waked!");
}
if (numup == 5 && numdown == 5) {
System.out.println("They Meet!");
System.exit(0);
}
}

}

public void run() {
while (true) {
if (Thread.currentThread().getName().equals("up")) {

numup++;
System.out.println(numup);

} else if (Thread.currentThread().getName().equals("down")) {

numdown--;
System.out.println(numdown);
}

try {
Thread.sleep(1000);
} catch (Exception e) {
System.out.println("wake!");
}
}

}
}


The successful result
It is strange and I don't know why.

Answer

This could be because of the CPU cache. When the number thread updates the value of the variable (this goes from its CPU cache to main memory) by then the CPU cache of the corresponding main thread might not have got updated.

So when main thread check's the value of the variable it was still the old value.

  1. You can use Volatile. OR
  2. Use AtomicInteger for these operations.

You can refer to this link.

In a multithreaded application where the threads operate on non-volatile variables, each thread may copy variables from main memory into a CPU cache while working on them, for performance reasons. If your computer contains more than one CPU, each thread may run on a different CPU. That means, that each thread may copy the variables into the CPU cache of different CPUs.

With non-volatile variables there are no guarantees about when the Java Virtual Machine (JVM) reads data from main memory into CPU caches, or writes data from CPU caches to main memory.

Volatile:

public static volatile int numup = 0;
public static volatile int numdown = 10;

Atomic Integer:

import java.util.concurrent.atomic.AtomicInteger;

public class Number implements Runnable {
    public static AtomicInteger numup = new AtomicInteger(0);
    public static AtomicInteger numdown = new AtomicInteger(10);

    public Number() {
    }

    public static void main(String args[]) {
        Number number = new Number();
        Thread T1 = new Thread(number, "up");
        Thread T2 = new Thread(number, "down");
        T1.start();
        T2.start();

        while (true) {
            if (numup.get() == 5 && numdown.get() == 5) {
                System.out.println("Meet!");
                System.exit(0);
            }
        }
    }

    public void run() {
        while (true) {
            if (Thread.currentThread().getName().equals("up")) {

                numup.incrementAndGet();
                System.out.println(numup);

            } else if (Thread.currentThread().getName().equals("down")) {

                numdown.decrementAndGet();
                System.out.println(numdown);
            }

            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                System.out.println("wake!");
            }
        }

    }
}
Comments