The following code snippet changes the Counter class to maintain individual counting as in each user counter will be incremented starting from 1.

The following code snippet changes the Counter class to maintain individual counting as in each user counter will be incremented starting from 1. So, the Counter will no longer be the shared resource. The CountingTask class is also modified to loop through each user 2 times as shown below. Is there anything wrong with the code shown below

The Counter class with individual counts

import java.util.HashMap;

import java.util.Map;

 

public class Counter {

 

 private Map<String, Integer> userToNumber = new HashMap<String, Integer>(10);

 

 public void increment() {

  Thread thread = Thread.currentThread();

  if (!userToNumber.containsKey(thread.getName())) {

   userToNumber.put(thread.getName(), Integer.valueOf(1));  //op1

  } else {

   Integer count = userToNumber.get(thread.getName());

   if (count != null) {

    ++count; // op2: increment it

    userToNumber.put(thread.getName(), count); //op3

   }

  }

}

 

 public Integer getCount(String name) {

  return userToNumber.get(name);

 }

}

 

// The counting task that repeats twice for each user

public class CountingTask implements Runnable {

 

 private Counter counter;

 

 public CountingTask(Counter counter) {

  super();

  this.counter = counter;

 }

 

public void run() {

 

  for (int i = 0; i < 2; i++) {

   counter.increment();

   Thread thread = Thread.currentThread();

   System.out.println(thread.getName() + ” value is “

     + counter.getCount(thread.getName()));

  }

 }

}

If each user will be accessed by only one thread, then the above code is thread-safe because each user will be operating on his/her data.

So, only one thread will access the map entry for User-1, and so on.

But, what happens if User-3 has two threads created as shown below.

The Thread 3 and 4 are User 3.

In this scenario, the above code is not thread safe, and it needs to be made atomic with one of the three options discussed above. It can be quite dangerous to assume that one user will be accessed only by one thread.What if in the future, additional threads are added to improve performance per user?

public class CountingManager

{

public static void main(String[] args) throws InterruptedException

{

 

Counter counter = new Counter(); // create an instance of the Counter

CountingTask task = new CountingTask(counter); // pass the counter to the runnable CountingTask

 

//Create 10 user threads (non-daemon) from the main thread that share the counter object

Thread thread1 = new Thread(task, “User-1”);

Thread thread2 = new Thread(task, “User-2”);

Thread thread3 = new Thread(task, “User-3”); //user 3

Thread thread4 = new Thread(task, “User-3”); //User 3

 

//start the threads

thread1.start();

thread2.start();

thread3.start();

thread4.start();

 

//observe the racing conditions in the output

}

}

If you don’t perform the operations 1 to 3 atomically (i.e. as a unit),

you will get an out put like:

User-1 value is 1

User-1 value is 2

User-3 value is 2

User-3 value is 3

User-3 value is 2

User-3 value is 4

User-2 value is 1

User-2 value is 2

As you can see, the User-3 has the value 2 repeated twice and value 1 is missing. If you apply the one of the options outlined above, you will get an output like:

User-1 value is 1

User-1 value is 2

User-3 value is 1

User-3 value is 2

User-2 value is 1

User-2 value is 2

User-3 value is 3

User-3 value is 4

Hence, the operations 1-3 need to be made atomic if accessed concurrently by multiple threads.Those three operations are

  • storing the initial value
  • incrementing the counter
  • storing the incremented value