How to add syncronization properly Java

后端 未结 4 1908
孤城傲影
孤城傲影 2021-01-27 17:56

As the integers are produced the Consumer thread sums their value (1+2+3…+10=55)

Producer thread generates integers from 1 to 10

The program is meant to produce

相关标签:
4条回答
  • 2021-01-27 18:42

    The reason why this does not work, and could not possibly work, is that your shared int is only capable of containing a single value. So, even if you made your getter and setter synchronized, (public synchronized void instead of public void, or using a private lock object) one thread could still write two values before the other thread gets the chance to read any, and one thread could read the same value twice before the other thread gets a chance to replace it with a new value.

    So, you have two options:

    • Make UsingSharedInt contain an Integer instead of an int, and:

      • Make the setter loop until the value is null before replacing it with a non-null value. This way, a value will never be overwritten.

      • Make the getter loop until the value is non-null before fetching it, and then set it to null before returning the value. This way, a value will never be read twice.

    • As Sasha Salauyou suggested, use a BlockingQueue; one thread adds integers to the queue, while the other thread removes integers from the queue and processes them. This is the best approach.

    0 讨论(0)
  • 2021-01-27 18:46

    Just use BlockingQueue as a container for elements that producer produces and consumer consumes:

    public class UsingSharedInt {
    
           private BlockingQueue<Integer> q = new ArrayBlockingQueue<>(100);
    
           public void setSharedInt( int val )
           {
              System.err.println( Thread.currentThread().getName() +
              " setting sharedInt to " + val );
              q.add(val); // puts val into the queue
           }
    
           public int getSharedInt()
           {
             int val = q.take(); // waits for element to become available in queue, then returns one
             System.err.println( Thread.currentThread().getName() +
             " retrieving sharedInt value " + val);
             return val;
           }
       }
    
    0 讨论(0)
  • 2021-01-27 18:48

    Just add a temp int in Consumer to see if it's different from the last one.

    int val, sum, tmp = 0;
    do {
    // sleep for a random interval
      try { 
         Thread.sleep( (int) ( Math.random() * 3000 ) ); 
      }catch( InterruptedException e ) {
         System.err.println( e.toString() );
      }
      val = cHold.getSharedInt();
      if(val!=tmp){
        sum += val;
      }
      tmp = val;
    } while ( val != 10 );
    
    0 讨论(0)
  • 2021-01-27 18:51

    The problem is not only concurrent access to the shared int. There is some queueing logic to look at.

    In the code, the Producer loops and set the value of SharedInt without waiting for it to be consumed by the Consumer. When the Consumer read a value, it will read some values between [1,10] and certainly the last one (10) since it is the exit condition in the while loop. But since each thread write/read the value at random times, you will not have the perfect sequence of write/read/write/read/etc. but something like write/write/read/write/write/read/read/etc...

    In order for this to work, you need to have the writing in the SharedInt to block after setting one value (or you need to queue the different values), until this value is read (consumed) by the Consumer thread. The same for reading by the Consumer, that must wait until a value is set by the producer.

    The easiest way to achieve that is to use a concurrent collection like BlockingQueue to store the shared integer. See the example of ProducerConsumer in the doc.

    You can implement this queue mechanism by yourself to experiment with low level sync, but it is not just a matter of putting a synchronized keyword around the SharedInt...

    0 讨论(0)
提交回复
热议问题