Java 线程化银行转账模拟与同步

Java 线程化银行转账模拟与同步,java,multithreading,Java,Multithreading,我有以下情况。我有一个帐户类,每个帐户都有一个余额,钱可以转移到它 public class Account { private int balance; public Account() { this.balance = 0; } public void transfer(int amount) { this.balance += amount; } @Override public String t

我有以下情况。我有一个帐户类,每个帐户都有一个余额,钱可以转移到它

public class Account {
    private int balance;

    public Account() {
        this.balance = 0;
    }

    public void transfer(int amount) {
        this.balance += amount;
    }

    @Override
    public String toString() {
        return "Account (balance: " + balance + ")";
    }
}
                    e.printStackTrace();
                }
            }
        };
        thread.start();
我有一个转会经理。转账只需两个账户和一笔金额即可。传输管理器可以通过将其添加到数组列表(一种传输队列)来发出传输。将所有传输添加到队列后,可以调用performTransfers方法,该方法在每次传输时调用performTransfer方法

import java.util.ArrayList;

public class TransferManager {
    private ArrayList<Transfer> openTransfers;
    private int issuedTransfers;
    private int performedTransfers;

    public TransferManager() {
        openTransfers = new ArrayList<Transfer>();
        issuedTransfers = 0;
        performedTransfers = 0;
    }

    public void issueTransfer(Account from, Account to, int amount) {
        openTransfers.add(new Transfer(from, to, amount));
        issuedTransfers++;
    }

    public void performTransfers() {
        for(Transfer transaction : openTransfers) {
            transaction.performTransfer();
            performedTransfers++;
        }       
        openTransfers.clear();
    }

    @Override
    public String toString() {
        return "TransferManager (openTransfers: " + openTransfers.size() + "; issuedTransfers: " + issuedTransfers + "; performedTransfers: " + performedTransfers + ")";
    }

    private static class Transfer {
        private Account from, to;
        private int amount;

        public Transfer(Account from, Account to, int amount) {
            this.from = from;
            this.to = to;
            this.amount = amount;
        }

        public void performTransfer() {
            from.transfer(-amount);
            to.transfer(amount);
        }
    }
}
                    e.printStackTrace();
                }
            }
        };
        thread.start();
知道我为什么会出现这些错误吗?同步在这里有什么帮助吗

                    e.printStackTrace();
                }
            }
        };
        thread.start();

                    e.printStackTrace();
                }
            }
        };
        thread.start();
(您可以放大以查看详细信息;))

                    e.printStackTrace();
                }
            }
        };
        thread.start();
转让经理:

                    e.printStackTrace();
                }
            }
        };
        thread.start();
银行测试:

                    e.printStackTrace();
                }
            }
        };
        thread.start();
开始:

                    e.printStackTrace();
                }
            }
        };
        thread.start();

在添加synchronized to issueTransfer和performTransfers方法后,我不断收到错误:

                    e.printStackTrace();
                }
            }
        };
        thread.start();

确定简单,所有线程,尝试执行此方法:

                    e.printStackTrace();
                }
            }
        };
        thread.start();
但是添加到ArrayList时没有同步。您必须理解,由于列表操作不是原子的,而且由于多个线程同时访问它,几乎任何事情都可能发生,并且列表会损坏

                    e.printStackTrace();
                }
            }
        };
        thread.start();
然后,当您试图读取列表时,您会在其中发现空元素,即使您没有要求首先插入一个元素。这是一个例子,说明了在没有正确处理的情况下访问相同的数据会损坏您的数据

                    e.printStackTrace();
                }
            }
        };
        thread.start();
编辑:

                    e.printStackTrace();
                }
            }
        };
        thread.start();
因此,每当您拥有共享状态并且希望使用多个线程访问它时,您必须同步。这不仅适用于issueTransfer方法

                    e.printStackTrace();
                }
            }
        };
        thread.start();
另一个问题是如何生成线程这与您最初的问题无关。

                    e.printStackTrace();
                }
            }
        };
        thread.start();
从这里的代码中,所有线程都访问一个全局状态:用于访问数组中帐户的索引。但是主线程使用for循环递增,而线程可以随时执行。在线程启动时,索引值已更改的风险很高

                    e.printStackTrace();
                }
            }
        };
        thread.start();

正如您所看到的,当您没有对并发进行足够的关注时,并发总是会咬到您;)

除Nicolas所说的之外-同步
issueTransfer
是不够的,因为主线程可能已经在
performTransfers
中,并且一些线程仍然可能卡在
issueTransfer
中。这意味着您的ArrayList仍由多个线程访问

                    e.printStackTrace();
                }
            }
        };
        thread.start();
可以创建锁对象并使用

                    e.printStackTrace();
                }
            }
        };
        thread.start();
synchronized (lock) {
   // vulnerable code goes here
}
保护可能被多个线程触及的代码段。
或者,您可以使相关方法(
issueTransfer
performtransfer
)同步。

如果没有行号,则有点难以详细说明。你能考虑在代码片段中添加行数(即使你的截图与引用的异常有不同的行数)吗?好的,我已经添加了源作为粘贴。我理解这一点,但是向那个方法头添加一个同步没有任何效果。我一直收到相同的错误向方法添加synchronized将阻止并行执行该方法。但是有几种方法可以做到这一点,因此必须在列表上进行同步。(同步块或在集合的帮助下使用并发列表#…)使用并发列表可能是个坏主意。它对给定的代码有帮助,但该类将非常容易受到攻击,因为其他变量(如
issuedTransfers
performedTransfers
)将不会同步。对任何共享状态的所有访问都必须同步并以一致的方式访问。因此,仅同步issueTransfer还不够我添加了synchronized语句,但仍然出现错误,请查看我的编辑。您的主要方法是什么样的?我使方法同步,但我的实例上没有NPE。结果仍然不是不正确/不确定的,但例外情况我无法复制。我已复制粘贴了你的主,我仍然无法复制NPE。与pastebin的唯一区别是我有两个同步的TransferManager方法。你确定你已经重新编译了代码吗?是的,我已经重新编译了,我已经压缩了:在你的zip中,有一些方法“public void synchronized”,它们甚至没有编译。正确的语法是“public synchronized void”。
                    e.printStackTrace();
                }
            }
        };
        thread.start();