Вот код из K & B Study Guide
class Account {
private int balance = 50;
public int getBalance() {
return balance;
}
public void withdraw(int amount) {
balance = balance - amount;
}
}
public class AccountDanger implements Runnable {
private Account acct = new Account();
public static void main (String [] args) {
AccountDanger r = new AccountDanger();
Thread one = new Thread(r);
Thread two = new Thread(r);
one.setName("Fred");
two.setName("Lucy");
one.start();
two.start();
}
public void run() {
for (int x = 0; x < 5; x++) {
makeWithdrawal(10);
if (acct.getBalance() < 0) {
System.out.println("account is overdrawn!");
}
}
}
private synchronized void makeWithdrawal(int amt) {
if (acct.getBalance() >= amt) {
System.out.println(Thread.currentThread().getName()
+ " is going to withdraw");
try {
Thread.sleep(500);
} catch(InterruptedException ex) { }
acct.withdraw(amt);
System.out.println(Thread.currentThread().getName()
+ " completes the withdrawal");
} else {
System.out.println("Not enough in account for "
+ Thread.currentThread().getName()
+ " to withdraw " + acct.getBalance());
}
}
}
Я получаю результат как
Однако результат книги выглядит следующим образом:
Любой может помочь мне объяснить, почему существует разница. Спасибо!
Это может произойти в любом порядке. Я предполагаю, что книга хочет показать:
Если есть несколько сторон, а именно. X
и Y
X is going to withdraw
, будет строго следовать X completes the withdrawal
и никогда не Y...
потому что оба эти утверждения находятся в synchronized
методе, который гарантирует, что один и только один поток может выполнять этот блок кода на данный момент времени.
Это из-за вашего синхронизированного блока только для функции makeWithdrawal. Сначала Fred вызывает makeWithdrawal и блокирует вызов этой функции в другом потоке, как только функция останавливается, каждый поток в очереди обращается к вызову. Это дело Люси. Чтобы Фред закончил свою работу, прежде чем позволить другому сделать, попытайтесь заблокировать все содержимое метода run, как это
public synchronized void run() {
for (int x = 0; x < 5; x++) {
System.out.println(Thread.currentThread().getName()
+ " Start calling");
makeWithdrawal(10);
if (acct.getBalance() < 0) {
System.out.println("account is overdrawn!");
}
}
}
Или
public void run() {
synchronized (this) {
for (int x = 0; x < 5; x++) {
System.out.println(Thread.currentThread().getName()+ " Start calling");
makeWithdrawal(10);
if (acct.getBalance() < 0) {
System.out.println("account is overdrawn!");
}
}
}
}
Я выполнил предоставленный вами код. И результат:
Fred is going to withdraw
Fred completes the withdrawal
Lucy is going to withdraw
Lucy completes the withdrawal
Fred is going to withdraw
Fred completes the withdrawal
Lucy is going to withdraw
Lucy completes the withdrawal
Lucy is going to withdraw
Lucy completes the withdrawal
Not enough in account for Fred to withdraw 0
Not enough in account for Fred to withdraw 0
Not enough in account for Fred to withdraw 0
Not enough in account for Lucy to withdraw 0
Not enough in account for Lucy to withdraw 0
Это совершенно другой результат, который вы упомянули выше. Когда один поток выполняет синхронизированный метод для объекта, все остальные потоки, которые вызывают синхронизированный метод для одного и того же объекта, блокируются до тех пор, пока не будет выполнен первый поток. так как когда синхронизированный метод завершается, он автоматически устанавливает связь между событиями и последующим вызовом синхронизированного метода для одного и того же объекта. Таким образом, выход различается каждый раз.
Когда я попробовал ваш код, NetBeans предупредил меня, что я использовал Thread.sleep
в синхронизированном блоке. Что происходит с ним, так это то, что, поскольку он синхронизирован, другой поток не может работать в то же время, и ожидание в основном ничего.
Я попытался поставить сон в цикле, и порядок соблюден (даже если Netbeans предупреждает меня о sleep in a loop
но на этот раз это бессмысленно)