Java: multiple threads decreasing a static variable through a synchronized method while accessing the variable

I am trying to synchronize the methods of the Person class so that my static counter variable decreases by one thread at a time.

 public class Person extends Thread { private static int count = 10; public void decrement() { synchronized(Person.class) { count--; } } public int getCount() { return count; } public void run(){ while( count > 0){ this.decrement(); System.out.print(this.getCount() + ","); } } } 

Here is my main class. Each thread will be reduced to a static counter using the synchronized method to avoid mutual access of threads to the same resource.

 public class Main { /** * @param args the command line arguments */ public static void main(String[] args) { Person p1 = new Person(); Person p2 = new Person(); Person p3 = new Person(); Person p4 = new Person(); Person p5 = new Person(); p1.start(); p2.start(); p3.start(); p4.start(); p5.start(); } } 

But when I run my program, it prints repeating counter values. What am I doing wrong?

Outputs:

 8,8,7,6,5,4,3,2,1,0 8,7,5,3,1,0,6,8,4,0 
+6
source share
3 answers

What happens in the source code:

  • Push one counter (count → 9)
  • Spend two abbreviations count (count -> 8)
  • Make three cuts (count → 7)
  • The number of four abbreviations count count (count → 6)
  • Enter one number of outputs (quantity: 6)
  • Enter the two outputs of count (count: 6)
  • Enter three conclusions (quantity: 6)
  • Stream of four outputs (quantity: 6)

Since you are blocking decrement, not decrement and output together, it appears several times.

In other words, there is no guarantee that this code will execute in the opposite way:

  this.decrement(); System.out.print(this.getCount() + ","); 

Here is the fixed code. It returns the current counter value with a decrease so that the new value can be returned and printed.

 public class Person extends Thread { private static int count = 10; public int decrement() { synchronized(Person.class) { count = count - 1; return count; } } public int getCount() { synchronized(Person.class) { return count; } } public void run(){ while( getCount() > 0){ int count = this.decrement(); System.out.println(count); } } } 

I would recommend AtomicInteger for this task:

 import java.util.concurrent.atomic.AtomicInteger; public class Person extends Thread { private static AtomicInteger count = new AtomicInteger(10); public int decrement() { return count.decrementAndGet(); } public void run(){ while(count.get() > 0){ int currentCount = this.decrement(); System.out.print(currentCount + ","); } } } 
+4
source

It is not enough to synchronize only the records, you also need to synchronize the getter (otherwise the reader thread may read the outdated value). But in this case, the problem is that other threads can alternate execution between the time the thread shrinks and the time that the same thread retrieves the value.

Use java.util.concurrent.atomic.AtomicInteger to store the account. But if you keep separate methods for decreasing and getting (decrement blocking and getter blocking separately), there is still nothing that guarantees that the streams will not alternate, which will cause duplicates to be recorded. Using the AtomicIntegerAndGet reduction method, verify that the value that has decreased is returned.

+3
source

Proper synchronization is the key, but using AtomicInteger is not the whole answer. What you need to understand is that each thread must report a counter that has just been decreased, which can be changed by another thread, even if you: 1) use AtomicInteger or 2) correctly (separately), synchronizing both decrement and getCount .

The body of the while is a critical section, a section of code that should not be interrupted.

 public void run(){ while( count > 0){ synchronized (Person.class) { decrement(); System.out.print(getCount() + ","); } } } 

Output:

 9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4 

Sometimes it stops at -3 . Now each instance can freely continue and decrease, because it checks the while condition that passes, then the thread is interrupted, then the other thread is reduced. Then the original thread decreases, even if it is already up to 0 ! Check inside the loop.

 public void run(){ while( count > 0){ synchronized (Person.class) { if (count > 0) { decrement(); System.out.print(getCount() + ","); } } } } 
+1
source

All Articles