Wayne
Wayne

Reputation: 13

How to add syncronization properly Java

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 an integer and consume it right away. But, the result generated at the program’s end rarely equals 55. This is because the threads don’t wait for each other to complete their tasks

Need to add syncronization to the code so that the consumer thread adds a value to the total only after a producer thread has generated a new integer

Driver.java

    public class Lab08_Driver {

      public static void main(String args[]) {

       UsingSharedInt h = new UsingSharedInt();
       Producer p = new Producer(h);
       Consumer c = new Consumer(h);

        p.start();
        c.start();
      }
    }

Consumer.java

  public class Consumer extends Thread {

     private UsingSharedInt cHold;


     public Consumer( UsingSharedInt h )
     {
       super( "ConsumeInteger" );
       cHold = h;
     }

     public void run()
     {
       int val, sum = 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();
       sum += val;
       } while ( val != 10 );

       System.err.println(
       getName() + " retrieved values totaling: " + sum +
       "\nTerminating " + getName() );
     }
  }

Producer.java

   public class Producer extends Thread {

      private UsingSharedInt pHold;

      public Producer( UsingSharedInt h )
      {
         super( "ProduceInteger" );
         pHold = h;
      }

      public void run()
      {
          for ( int count = 1; count <= 10; count++ ) {
          // sleep for a random interval
          try {
              Thread.sleep( (int) ( Math.random() * 3000 ) );
          }
          catch( InterruptedException e ) {
              System.err.println( e.toString() );
          }

         pHold.setSharedInt( count );
       }

       System.err.println( getName() +
       " finished producing values" +
       "\nTerminating " + getName() );
     }
   }

UsingSharedInt.java

    // HoldIntegerUnsynchronized.java

    public class UsingSharedInt {

       private int sharedInt = -1;

       public void setSharedInt( int val )
       {
          System.err.println( Thread.currentThread().getName() +
          " setting sharedInt to " + val );
          sharedInt = val;
       }

       public int getSharedInt()
       {
         System.err.println( Thread.currentThread().getName() +
         " retrieving sharedInt value " + sharedInt );
         return sharedInt;
       }
   }

Upvotes: 0

Views: 102

Answers (3)

TV Trailers
TV Trailers

Reputation: 25

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 );

Upvotes: 0

T.Gounelle
T.Gounelle

Reputation: 6033

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...

Upvotes: 0

Alex Salauyou
Alex Salauyou

Reputation: 14338

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;
       }
   }

Upvotes: 1

Related Questions