为什么我们声明这个方法是同步的

时间:2018-02-25 22:21:40

标签: java concurrency

考虑这个课程:

// Synchronizing access to shared mutable data using Object 
// methods wait and notifyAll.
public class SynchronizedBuffer implements Buffer
{
   private int buffer = -1; // shared by producer and consumer threads
   private boolean occupied = false;

   // place value into buffer
   public synchronized void blockingPut(int value) 
      throws InterruptedException
   {
      // while there are no empty locations, place thread in waiting state
      while (occupied) 
      {
         // output thread information and buffer information, then wait
         System.out.println("Producer tries to write."); // for demo only
         displayState("Buffer full. Producer waits."); // for demo only
         wait();
      }

      buffer = value; // set new buffer value

      // indicate producer cannot store another value
      // until consumer retrieves current buffer value
      occupied = true;

      displayState("Producer writes " + buffer); // for demo only

      notifyAll(); // tell waiting thread(s) to enter runnable state
   } // end method blockingPut; releases lock on SynchronizedBuffer 

   // return value from buffer
   public synchronized int blockingGet() throws InterruptedException
   {
      // while no data to read, place thread in waiting state
      while (!occupied)
      {
         // output thread information and buffer information, then wait
         System.out.println("Consumer tries to read."); // for demo only
         displayState("Buffer empty. Consumer waits."); // for demo only
         wait();
      }

      // indicate that producer can store another value 
      // because consumer just retrieved buffer value
      occupied = false;

      displayState("Consumer reads " + buffer); // for demo only

      notifyAll(); // tell waiting thread(s) to enter runnable state

      return buffer;
   } // end method blockingGet; releases lock on SynchronizedBuffer 

   // display current operation and buffer state; for demo only
   private synchronized void displayState(String operation)
   {
      System.out.printf("%-40s%d\t\t%b%n%n", operation, buffer, 
         occupied);
   } 
} // end class SynchronizedBuffer

本书中的这一段:

  

请注意,方法displayState是一个同步方法。这很重要,因为它也会读取SynchronizedBuffer的共享内容   可变数据。虽然一次只有一个线程可以获得给定的   对象的锁定,一个线程可以获取同一个对象的锁定多个   times - 这称为可重入锁定并启用一个同步   在同一个对象上调用另一个对象的方法。

为什么我们将方法displayState()声明为synchronized,尽管它只是从同步方法调用,因此当调用它时调用线程已经在对象上有监视器锁?

1 个答案:

答案 0 :(得分:0)

您对此源代码提出质疑。当方法拥有对象的监视器时,输入synchronized方法或阻止再次获取同一监视器,根本没有效果。

作者也不太可能认为未来对代码的更改可能会在没有拥有对象监视器的情况下调用该方法。首先,该方法的全部目的是报告正在进行的操作,其次,实施正确的使用可以实现更简单。

该方法使用不一致也很有趣。该方法总是报告buffer的当前值,但是在调用方法之前,四个调用者中的两个冗余地将当前值buffer附加到操作参数字符串。然后,在两个调用者之前还有其他显式打印语句。由于他们的消息是“[生产者|消费者] 尝试到[写入|读取]”,这些语句很可能应该在循环之前报告尝试,而不是在内部,在一个已经知道尝试失败的地方。

public class SynchronizedBuffer //implements Buffer
{
    private int buffer = -1; // shared by producer and consumer threads
    private boolean occupied = false;

    // place value into buffer
    public synchronized void blockingPut(int value) 
       throws InterruptedException
    {
        System.out.println("Producer tries to write."); // for demo only
        // while there are no empty locations, place thread in waiting state
        while(occupied) 
        {
           // output thread information and buffer information, then wait
           displayState("Buffer full. Producer waits.", buffer, occupied);// demo only
           wait();
        }

        buffer = value; // set new buffer value

        // indicate producer cannot store another value
        // until consumer retrieves current buffer value
        occupied = true;

        displayState("Producer writes ", buffer, occupied); // for demo only

        notifyAll(); // tell waiting thread(s) to enter runnable state
    } // end method blockingPut; releases lock on SynchronizedBuffer 

    // return value from buffer
    public synchronized int blockingGet() throws InterruptedException
    {
        System.out.println("Consumer tries to read."); // for demo only
        // while no data to read, place thread in waiting state
        while(!occupied)
        {
            // output thread information and buffer information, then wait
            displayState("Buffer empty. Consumer waits.", buffer, occupied);// demo only
            wait();
        }

        // indicate that producer can store another value 
        // because consumer just retrieved buffer value
        occupied = false;

        displayState("Consumer reads ", buffer, occupied); // for demo only

        notifyAll(); // tell waiting thread(s) to enter runnable state

        return buffer;
    } // end method blockingGet; releases lock on SynchronizedBuffer 

    // display current operation and buffer state; for demo only
    // not accessing the object, hence no synchronization needed
    private static void displayState(String operation, int buffer, boolean occupied)
    {
       System.out.printf("%-40s%d\t\t%b%n%n", operation, buffer, occupied);
    } 
}

通过将要报告的状态转换为参数,以线程安全的方式访问它们的责任显然在于调用者,并且通过创建方法static,该方法不可能错误地访问对象状态。此外,变量值的冗余报告将在呼叫站点变得明显。