什么时候真正需要 volatile 关键字?

问题描述 投票:0回答:1

此代码取自 Jeff Friesen 的书 Java 线程和并发实用程序

public class PC
{
   public static void main(String[] args)
   {
      Shared s = new Shared();
      new Producer(s).start();
      new Consumer(s).start();
   }
}

class Shared
{
   private char c;
   private volatile boolean writeable = true;

   synchronized void setSharedChar(char c)
   {
      while (!writeable)
         try
         {
            wait();
         }
         catch (InterruptedException ie) 
         {
         }
      this.c = c;
      writeable = false;
      notify();
   }

   synchronized char getSharedChar()
   {
      while (writeable)
         try
         {
            wait();
         }
         catch (InterruptedException ie) 
         {
         }
      writeable = true;
      notify();
      return c;
   }
}

class Producer extends Thread
{
   private final Shared s;

   Producer(Shared s)
   {
      this.s = s;
   }

   @Override
   public void run()
   {
      for (char ch = 'A'; ch <= 'Z'; ch++)
      {
         synchronized(s)
         {
            s.setSharedChar(ch);
            System.out.println(ch + " produced by producer.");
         }
      }
   }
}
class Consumer extends Thread
{
   private final Shared s;

   Consumer(Shared s)
   {
      this.s = s;
   }

   @Override
   public void run()
   {
      char ch;
      do
      {
         synchronized(s)
         {
            ch = s.getSharedChar();
            System.out.println(ch + " consumed by consumer.");
         }
      }
      while (ch != 'Z');
   }
}

我不明白为什么有必要将

volatile
设置为
writeable
字段,因为它只能通过
synchronized
方法访问,这与易失性(可见性+写入互斥)具有相同的效果。为什么在这种情况下字段
writeable
需要是
volatile
而不是字段
c

对于带有

balance
字段的下一个代码也有同样的问题:为什么它需要是易失性的,因为它已经通过
synchronized
withdraw
方法访问了。

public class CheckingAccount
{
   private volatile int balance;

   public CheckingAccount(int initialBalance)
   {
      balance = initialBalance;
   }

   public synchronized boolean withdraw(int amount)
   {
      if (amount <= balance)
      {
         try
         {
            Thread.sleep((int) (Math.random() * 200));
         }
         catch (InterruptedException ie)
         {
         }
         balance -= amount;
         return true;
      }
      return false;
   }

   public static void main(String[] args)
   {
      final CheckingAccount ca = new CheckingAccount(100);
      Runnable r = new Runnable()
                   {
                      @Override
                      public void run()
                      {
                         String name = Thread.currentThread().getName();
                         for (int i = 0; i < 10; i++)
                             System.out.println (name + " withdraws $10: " +
                                                 ca.withdraw(10));
                      }
                   };
      Thread thdHusband = new Thread(r);
      thdHusband.setName("Husband");
      Thread thdWife = new Thread(r);
      thdWife.setName("Wife");
      thdHusband.start();
      thdWife.start();
   }
}

有人可以解释一下吗?

java concurrency
1个回答
1
投票

您正确理解了这些想法。

volatile
在这里完全没用,不应该使用。如果本书没有介绍这些片段作为 not 做什么的示例,并且您还粘贴了整个片段(即,您没有省略访问这些 is not
synchronized
上的字段的方法
this 
)-那么这就是书中一个非常痛苦的错误。如果这是由于作者本身不了解 JMM 造成的错误,那就意味着这本书应该被扔进垃圾桶。如果这只是示例中的一个错误,也许是因为它最初是作为使用
volatile
的示例,后来改用
synchronized
,但作者忘记删除
volatile
,这仍然是一个相当严重的编辑错误.

© www.soinside.com 2019 - 2024. All rights reserved.