如何正确添加同步 Java



当整数生成时,消费者线程对它们的值求和(1+2+3...+10=55)

生产者线程生成从 1 到 10 的整数

该程序旨在生成一个整数并立即使用它。但是,在程序结束时生成的结果很少等于 55。这是因为线程不会等待彼此完成其任务

需要向代码添加同步,以便使用者线程仅在生产者线程生成新整数后才向总数添加一个值

驱动程序.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();
      }
    }

消费者.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() );
     }
  }

制片人.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() );
     }
   }

使用共享.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;
       }
   }

只需将 BlockingQueue 用作生产者产生和消费者消费的元素的容器:

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

问题不仅在于对共享 int 的并发访问。有一些排队逻辑需要查看。

在代码中,Producer循环并设置 SharedInt 的值,而无需等待Consumer使用它。当Consumer读取一个值时,它将读取 [1,10] 之间的一些值,当然还有最后一个 (10),因为它是 while 循环中的退出条件。但是由于每个线程都是随机时间写入/读取值,因此您将不会有完美的写入/读取/写入/读取/等序列,而是像

写/写/读/写/写/读/读/等......

为了使其正常工作,您需要在设置一个值后阻止SharedInt中的写入(或者您需要对不同的值进行排队),直到该值被Consumer线程读取(使用)。Consumer读取也是如此,必须等到生产者设置一个值。

实现此目的的最简单方法是使用并发集合(如 BlockingQueue)来存储共享整数。请参阅文档中的生产者消费者示例。

您可以自己实现此队列机制来尝试低级同步,但这不仅仅是在SharedInt周围放置一个synchronized关键字的问题......

只需在 Consumer 中添加一个 temp int,看看它是否与上一个不同。

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

最新更新