Java wait notify with shared integer object

user3157908 :

I am trying to print numbers from 1 to 10 in sequence using a shared integer object across multiple threads. When using the shared object as AtomicInteger, the program works correctly, but when using normal Integer objects, the program throws an exception and I don't know why this is happening.

Program with AtomicInteger

import java.util.concurrent.atomic.AtomicInteger;  

public class ThreadingProblem {

  public static void main(String[] args) {  
    AtomicInteger sharedInt = new AtomicInteger(0);  
    Thread t1 = new Thread(new ThreadingPrintingTask(sharedInt), "PrinterThread");  
    Thread t2 = new Thread(new ThreadingIncrementingTask(sharedInt), "IncrementerThread");  
    t1.start();  
    t2.start();  
  }  
}  

class ThreadingPrintingTask implements Runnable {    

  private AtomicInteger sharedObject;  

  public ThreadingPrintingTask(AtomicInteger sharedObject) {  
    this.sharedObject = sharedObject;  
  }  

  @Override  
  public void run() {  
    try {  
      synchronized (sharedObject) {  
        while (true) {  
          sharedObject.wait();  
          System.out.println("Shared object value is: " + sharedObject);  
          sharedObject.notify();  
        }  
      }  
    }  
    catch (InterruptedException e) {  
    }  

  }  
}  

class ThreadingIncrementingTask implements Runnable {  

  private AtomicInteger sharedObject;  

  public ThreadingIncrementingTask(AtomicInteger sharedObject) {  
    this.sharedObject = sharedObject;  
  }  

  @Override  
  public void run() {  
    synchronized (sharedObject) {  
      while (this.sharedObject.get() < 10) {  
        this.sharedObject.incrementAndGet();  
        this.sharedObject.notify();  
        try {  
          this.sharedObject.wait();  
        }  
        catch (InterruptedException e) {  
          // TODO Auto-generated catch block  
          e.printStackTrace();  
        }  
      }  
    }  
  }  
}  

Output

Shared object value is: 1
Shared object value is: 2
Shared object value is: 3
Shared object value is: 4
Shared object value is: 5
Shared object value is: 6
Shared object value is: 7
Shared object value is: 8
Shared object value is: 9
Shared object value is: 10

Program with normal Integer object

public class ThreadingProblem {

  public static void main(String[] args) {
    Integer sharedInt = new Integer(0);
    Thread t1 = new Thread(new ThreadingPrintingTask(sharedInt), "PrinterThread");
    Thread t2 = new Thread(new ThreadingIncrementingTask(sharedInt), "IncrementerThread");
    t1.start();
    t2.start();
  }
}

class ThreadingPrintingTask implements Runnable {

  private Integer sharedObject;

  public ThreadingPrintingTask(Integer sharedObject) {
    this.sharedObject = sharedObject;
  }

  @Override
  public void run() {
    try {
      synchronized (sharedObject) {
        while (true) {
          sharedObject.wait();
          System.out.println("Shared object value is: " + sharedObject);
          sharedObject.notify();
        }
      }
    }
    catch (InterruptedException e) {
    }

  }
}

class ThreadingIncrementingTask implements Runnable {

  private Integer sharedObject;

  public ThreadingIncrementingTask(Integer sharedObject) {
    this.sharedObject = sharedObject;
  }

  @Override
  public void run() {
    synchronized (sharedObject) {
      while (this.sharedObject < 10) {
        this.sharedObject++;
        this.sharedObject.notify();
        try {
          this.sharedObject.wait();
        }
        catch (InterruptedException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
      }
    }
  }
}

Output

Exception in thread "IncrementerThread" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at com.itiviti.apps.catalys.shared.mock.ThreadingIncrementingTask.run(ThreadingProblem.java:52)
    at java.lang.Thread.run(Unknown Source)
biziclop :

this.sharedObject++; does not do what you assumed it would do.

Since Integer is immutable, it can't change the existing shared object. What this operation does instead is unbox the value into an int, increment it, then box it back into a different Integer instance.

So your code is (almost*) equivalent to the following:

int temp = this.sharedObject.intValue();
temp = temp + 1;
this.sharedObject = new Integer(temp);

As at this point your object is no longer the same instance, your synchronized blocks won't line up with the wait()/notify() calls.

Note that this has got nothing to do with the atomicity of AtomicInteger, it's simply to do with how the ++ operator works on an Integer.

*In reality you might get a cached instance instead of new Integer(), but it will still be a different instance, as it represents a different int value.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=112497&siteId=1