【问题标题】:Locks and synchronized in javajava中的锁和同步
【发布时间】:2021-01-18 23:35:31
【问题描述】:

我目前正在使用 java 中的锁和同步方法。我有一个包含三个类的示例程序:一个帐户,具有两种取款和存款方法以及一个共享的可变余额;以及 Spender 和 Saver 类。

class Account{

    private float balance = 0;
    public void depositMoney(float amount){
    float tmp;
    synchronized(this){
        tmp = balance;
        System.out.println("(Adding money): the initial balance is: "
                   + tmp);
        tmp +=amount;
        balance = tmp;
        System.out.println("(Adding money): the final balance is: "
                   + balance);
        this.notify();
    }
    }
    
    public void withdrawMoney(float amount){
    float tmp;
    
    synchronized(this){

        while (balance <= 0){
        try {
            this.wait();
        } catch (Exception e) {}
        }
        
        tmp =  balance;
        System.out.println("(Withdrawing money): the initial balance is: "
                   + tmp);
        tmp -=amount;
        balance = tmp;
        System.out.println("(Withdrawing money): the final balance is: "
                   + balance);
        
    }
    }

}
class Saver extends Thread{

    private Account account;
    
    Scrooge(Account account){
    this.account = account;
    }
    
    public void run(){
    for (int i=0; i < 2; i++){
        account.depositMoney(1200);
    }
    }
}
class Spender extends Thread{

    private Account account;
    
    Donald(Account account){
    this.account = account;
    }
    
    public void run(){
    for (int i=0; i< 2; i++){
        account.withdrawMoney(400);
    }
    }
}
public class Bankv4{

    public static void main(String[] args){
    Account account = new Account();
    Spender s1 = new Spender(account);
    Spender s2 = new Spender(account);
    Spender s3 = new Spender(account);
    Saver saver = new Saver(account);
    s1.start();
    s2.start();
    s3.start();
    saver.start();
    }
}

问题是这段代码并不总是有效。它适用于 Saver 类运行 depositMoney 方法 30 次,数量为 100,Spender 类运行提款 10 次,数量为 100 的情况(因为有 3 个 Spender 线程 10 * 3 * 100 = 3000, Saver 类存入账户的金额)。

在上面的代码中,问题是虽然 Saver 类存入的数量与 Spender 提取的数量相同,但它的迭代次数较少,这会导致死锁,因为 Spender 线程运行 wait() 但 Saver 类由于其线程已结束,not run notify() 不运行,导致程序无法完成。

谁能解决这个问题?提前致谢

【问题讨论】:

  • 小问题:实现 Runnable 比扩展 Thread 更好。最好还是使用更新的 Java 更高级别的并发对象
  • 另一个明显的狡辩是,这不是现实的代码,没有人会真正以这种方式编写线程。尝试实现一些线程原语可能更有用,例如信号量或线程安全队列。 (但我意识到这可能只是一个例子。)

标签: java multithreading


【解决方案1】:

四件事。

  1. 使用ExecutorService 启动多个线程。
  2. 请致电notifyAll() 而不是notify()
  3. 捕获 InterruptedException 而不是 Exception 并至少打印一些内容以便您知道它发生了。
  4. 由于我使用的是ExecutorService,所以SaverSpender类不需要扩展Thread,只需实现Runnable即可。

我只更改了 AccountBankv4 类 — 即使根据您问题中的代码,SaverSpender 类不编译。 (我把这个问题留给 OP :-)

public class Account {
    private float balance = 0;

    public void depositMoney(float amount) {
        float tmp;
        synchronized (this) {
            tmp = balance;
            System.out.println("(Adding money): the initial balance is: " + tmp);
            tmp += amount;
            balance = tmp;
            System.out.println("(Adding money): the final balance is: " + balance);
            this.notifyAll(); // CHANGED THIS
        }
    }

    public void withdrawMoney(float amount) {
        float tmp;
        synchronized (this) {
            while (balance <= 0) {
                try {
                    this.wait();
                }
                catch (InterruptedException e) {
                    System.err.println("'Account.withdrawMoney()' interrupted."); // ADDED THIS
                }
            }
            tmp = balance;
            System.out.println("(Withdrawing money): the initial balance is: " + tmp);
            tmp -= amount;
            balance = tmp;
            System.out.println("(Withdrawing money): the final balance is: " + balance);

        }
    }
}

public class Bankv4 {

    public static void main(String[] args) {
        Account account = new Account();
        Spender s1 = new Spender(account);
        Spender s2 = new Spender(account);
        Spender s3 = new Spender(account);
        Saver saver = new Saver(account);
        ExecutorService es = Executors.newFixedThreadPool(4);
        es.execute(s1);
        es.execute(s2);
        es.execute(s3);
        es.execute(saver);
        es.shutdown();
    }
}

【讨论】:

    【解决方案2】:

    当储户存钱时,notify() 只释放一个等待线程。醒来的消费者不会花掉所有的钱,但没有什么能唤醒新的消费者来花掉剩下的钱。在最后一笔存款后,剩余的消费者将永远等待。

    您有 2 个选择来解决此问题。

    安全的方法是始终使用notifyAll() 而不是通知。这样,所有的消费者都会醒来,抓住锁,然后再次尝试消费。

    更高效的方法是在移除资金后让withdraw() 方法调用notify()。这种方式只会唤醒一个消费者,但这是一种危险的技术,因为您必须确定每个可能正在等待的线程在必要时肯定会唤醒另一个线程。

    【讨论】:

      猜你喜欢
      • 2011-04-26
      • 2019-06-01
      • 2013-04-25
      • 2011-07-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多