dinigo
dinigo

Reputation: 7428

How to update immediately shared variables between threads

My test program asks for a string and prints it every 2 seconds. I've read some already about java memory model or how threads don't immediately update their variables on the main memory.

I've tried with volatile and static attributes. Synchronizing code blocks in which the variable line was modified. With wait()/notifi() when the variable was changed and some others. How do I assign line as a reference and not as value? Am I using the methods I tried wrong? Why can objects stay perfectly synchronized when they act as monitors but they can't be when acting as pointers?

public class TestSharedVariable {
    static String line= "";

    public static void main(String[] args) {
        // For reading from keyboard
        Scanner keyboard = new Scanner(System.in);

        Printer p = new Printer(line);
        p.start();

        // Reads a line from keyboard into the shared variable
        while(!line.equals("quit")){
            line = keyboard.nextLine(); 

        }
    }
}

class Printer extends Thread{
    private volatile String line;

    public Printer(String palabra){
        this.line = palabra;
    }

    /*  Prints the line each 2 sec  */
    @Override
    public  void run(){
        while(!line.equals("quit")){
            try {
                sleep(2000);
            } catch (InterruptedException e) {e.printStackTrace();}
            System.out.println(this.getName() + ": " + line);
        }
    }
}

Output:

    Thread-0: 
    asdf
    Thread-0: 
    Thread-0: 

Upvotes: 2

Views: 2552

Answers (1)

Hovercraft Full Of Eels
Hovercraft Full Of Eels

Reputation: 285403

You have two main issues:

  • For one Strings are immutable, and so you cannot change the state of a String variable, such as the characters that comprise it, once it has been created, and
  • You are assigning two reference variables to the same object, but are expecting one variable to change its assignment if the other variable's assignment changes, and this isn't how Java reference variables work.

In other words, even though you're initially setting the Printer object's line variable to the same reference as the TestSharedVariable's static line variable, changing the TestSharedVariable's static line variable's reference later will have no effect on Printer line variable's reference assignment. This has nothing to do with threading and all to do with understanding reference variables. Think of reference variables like pointers in C or other similar languages. If two variables point to the same object, changing one variable to point to a different object will have no effect on the first variable.

For your code to work, both variables must share a reference to the same mutable object, and you must change the state of the mutable object, not the reference to it.

For e.g.,

import java.util.Scanner;

public class TestSharedVariable {
   static volatile MutableObject mutableObject = new MutableObject();

   public static void main(String[] args) {
      // For reading from keyboard
      Scanner keyboard = new Scanner(System.in);

      Printer p = new Printer(mutableObject);
      new Thread(p, "Print thread").start();

      // Reads a line from keyboard into the shared variable
      while (!mutableObject.getData().equals("quit")) {
         mutableObject.setData(keyboard.nextLine());

      }
   }
}

class Printer implements Runnable {
   private volatile MutableObject mutableObject;

   public Printer(MutableObject mutableObject) {
      this.mutableObject = mutableObject;
   }

   /* Prints the line each 2 sec */
   @Override
   public void run() {
      while (!mutableObject.getData().equals("quit")) {
         try {
            Thread.sleep(2000);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
         Thread thread = Thread.currentThread();
         System.out.println(thread.getName() + ": " + mutableObject);
      }
   }
}

class MutableObject {
   private String data = "";

   public String getData() {
      return data;
   }

   public void setData(String data) {
      this.data = data;
   }

   @Override
   public String toString() {
      return data;
   }

}

Upvotes: 3

Related Questions