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