7

in my program I need to have multiple threads use and edit the same variable, but it doesn't seem to be working. Here is an example of what I mean, this would be my main class.

public class MainClass {

  public static int number = 0;
  public static String num = Integer.toString(number);

  public static void main(String[] args) {
    Scanner in = new Scanner(System.in);
    System.out.println("Enter number of threads.");
    int threads = in.nextInt();
    for (int n = 1; n <= threads; n++) {
      java.lang.Thread t = new Thread();
      t.start();
    }
  }
}

This would be my Thread class:

public class Thread extends java.lang.Thread
{
  public void run()
  {
      MainClass.number++;
      System.out.println("Thread started");
      System.out.println(MainClass.num);
  }
}

I wrote this code on the spot, so there may be some errors, but thats ok. My program basically needs to do something like this, but instead of printing the number plus 1 every time, all the threads simply print the same number, 0, multiple times. Please help me, thanks.

Gray
  • 115,027
  • 24
  • 293
  • 354
user1947236
  • 673
  • 3
  • 12
  • 27

2 Answers2

12

In my program I need to have multiple threads use and edit the same variable, but it doesn't seem to be working...

Anytime multiple threads are updating the same variable you need to worry about memory synchronization. One of the ways that threads get high performance is because each thread utilizes the local CPU memory cache and so may be working with stale copies of variables. You need to use the synchronized or volatile keywords to force the thread's cache to write any updates to central storage or update its cache from central.

Although this takes care of memory synchronization, it doesn't necessarily protect you from race conditions. It is also important to realize that ++ is actually 3 operations: get the current value, increment it, and store it back again. If multiple threads are trying to do this, there are thread race-conditions which can cause the ++ operations to be missed.

In this case, you should use the AtomicInteger class which wraps a volatile int field. It gives you methods like incrementAndGet() which do the job of incrementing that field in a thread-safe manner.

public static AtomicInteger number = new AtomicInteger(0);
...
MainClass.number.incrementAndGet();

Multiple threads can then be incrementing the same variable safely.

Gray
  • 115,027
  • 24
  • 293
  • 354
  • Thanks for the answer. How would I convert an AtomicInteger to a string, because I need to be able to add a "0" before the number if the number is only 1 digit, so 5 would turn into 05. – user1947236 Feb 20 '13 at 15:45
  • `number.get()` returns an integer like normal. So you could do `"0" + number.get()`. – Gray Feb 20 '13 at 15:47
  • But I would need a way to check how many digits are in the number so I can only do it if it has 1 digit. I am currently using the length of the string after the int is converted. – user1947236 Feb 20 '13 at 15:48
  • Sure. Just say `int num = number.get();` and then do it like you would an integer @user1947236. – Gray Feb 20 '13 at 15:49
  • hi @Gray, what would happen IF LITERALLY 2 threads updated the same variable at the SAME TIME, could it happen or would it LITERALLY GIVE ERROR and hence the use of synchronization? I still find it hard to understand if THAT CASE COULD HAPPEN. THANKS –  Oct 31 '22 at 16:13
  • 2
    What happens is that the two threads have different versions of the shared object in their local cached memory. Remember that all CPU updates are cached. If 2 CPUs tried to update the same _central_ memory row at the SAME TIME then one would win because of memory locking. The 2nd update would win. @GeorgeMeijer – Gray Oct 31 '22 at 16:56
  • hi @Gray thanks, 1) do you mean to say that in the end it boils down to an update that comes AFTER overwrites a previous one? 2) is memory locking already a proprietary concept of the OS (it does it behind the scenes)? –  Oct 31 '22 at 17:25
  • Hi @Gray "If 2 CPUs tried to update the same central memory row at the SAME TIME then one would win because of memory locking. The 2nd update would win" Is that why you say that operations++ CAN BE LOST and therefore it is important to synchronize threads so that only one thread accesses a particular variable AT A TIME and THUS NO LOST updates?, Thanks +1 –  Oct 31 '22 at 18:03
  • 1
    Uh, partly @Daniel. ++ are 3 operations (get, increment, set). There is a race condition that can result in something t1-get, t2-get, t1-incr, t1-set, t2-incr, t2-set which will result in the variable being +1 and not +2. So even if there isn't 2 sets at the same time you can lose an increment. You have to recognize that threads are asynchronous and we want them to be that way for speed. – Gray Oct 31 '22 at 18:35
  • ok thanks @Gray +1, one last question, when there are 2 sets at EXACTLY the same time, ONLY ONE WINS and ONE is lost? –  Oct 31 '22 at 21:06
  • 1
    No, they both happen @Daniel, it's just that one happens and then is overwritten by the other. – Gray Nov 01 '22 at 00:35
  • In the example you gave me "t1-get, t2-get, t1-incr, t1..." what happened is that both read (captured-hold) the same value at the same time, that's why the sum was 1 as you said, right? @Gray –  Nov 01 '22 at 00:57
  • 1
    It doesn't happen at the same time. Computers can do things at the same time in different processors but as soon as they go to access disk or memory, things happen serially. One of the two CPUs would be blocked by hardware and so one CPU would make the update and then the other would make the update, overwriting the first. – Gray Nov 01 '22 at 01:12
  • "but as soon as they go to access disk or memory, things happen serially" This information is WORTH GOLD, that's what I couldn't understand you. With this, it is NOW CLEAR to me the "lost" updates. Thank you very much @Gray –  Nov 01 '22 at 01:28
0

Here You go...

 import java.util.Scanner;
    import java.util.concurrent.atomic.AtomicInteger;

    public class UpdateVariables
    {
        static int num = 0;
        public static AtomicInteger  atomicInteger = new AtomicInteger(num);


        @SuppressWarnings("resource")
        public static void main(String args[])
        {
            Scanner userInput = new Scanner(System.in);
            System.out.println("Enter Number of Threads: ");
            int getThreadNumber = userInput.nextInt();
            for(int i = 0; i < getThreadNumber; i++)
            {
                PrintThread p = new PrintThread();
                p.start();
            }

        }

    }

    class PrintThread extends Thread
    {
        public void run()
        {
            System.out.println("Thread Started: ");
            System.out.println(UpdateVariables.atomicInteger.incrementAndGet());

        }
    }