简体   繁体   English

编写在多线程Java环境中更新两个对象的方法的最佳方法?

[英]Best ways to write a method that updates two objects in a multithreaded java environment?

Suppose we have a class called AccountService that manages the state of accounts. 假设我们有一个名为AccountService的类来管理帐户状态。

AccountService is defined as AccountService定义为

interface AccountService{
 public void debit(account);
 public void credit(account);
 public void transfer(Account account, Account account1);

}

Given this definition, what is the best way to implement transfer() so that you can guarantee that transfer is an atomic operation. 鉴于此定义,实现transfer()的最佳方法是什么,以便您可以保证传输是原子操作。

I'm interested in answers that reference Java 1.4 code as well as answers that might use resources from java.util.concurrent in Java 5 我对引用Java 1.4代码的答案以及可能使用Java 5中的java.util.concurrent资源的答案感兴趣

Synchronize on both Account objects and do the transfer. 同步两个Account对象并执行传输。 Make sure you always synchronize in the same order. 确保始终以相同的顺序同步。 In order to do so, make the Account s implement Comparable , sort the two accounts, and synchronize in that order. 为此,请使Account实现Comparable ,对两个帐户进行排序,然后按该顺序进行同步。

If you don't order the accounts, you run the possibility of deadlock if one thread transfers from A to B and another transfers from B to A. 如果您没有订购帐户,如果一个线程从A转移到B而另一个从B转移到A,则会出现死锁的可能性。

This exact example is discussed on page 207 of Java Concurrency in Practice , a critical book for anybody doing multi-threaded Java development. 这个确切的例子在Java Concurrency in Practice的第207页讨论,这是任何进行多线程Java开发的人的重要书籍。 The example code is available from the publisher's website : 示例代码可从发布者的网站获得

You probably need to have a full transactions support (if it's a real application of course). 您可能需要获得完整的事务支持(如果它是一个真正的应用程序)。

The difficulty of solution hardly depends on your environment. 解决方案的难度很大程度上取决于您的环境。 Describe your system in detail and we'll try to help you (what kind of application? does it use web-server? which web-server? what is used to store data? and so on) 详细描述您的系统,我们会尽力帮助您(什么样的应用程序?它使用Web服务器?哪个Web服务器?什么用于存储数据?等等)

If you can guarantee that all accesses are made through the transfer method, then probably the easiest approach is just to make transfer a synchronized method. 如果您可以保证所有访问都是通过传输方法进行的,那么最简单的方法可能就是使传输成为同步方法。 This will be thread-safe because this guarantees that only one thread will be running the transfer method at any one time. 这将是线程安全的,因为这可以保证在任何时候只有一个线程将运行传输方法。

If other methods may also access the AccountService, then you might decide to have them all use a single global lock. 如果其他方法也可以访问AccountService,那么您可能决定让它们都使用单个全局锁。 An easy way of doing this is to surround all code that accesses the AccountService in a synchronized (X) {...} block where X is some shared / singleton object instance (that could be the AccountService instance itself). 一种简单的方法是在同步(X){...}块中包围访问AccountService的所有代码,其中X是一些共享/单一对象实例(可能是AccountService实例本身)。 This will be thread safe because only one thread will be accessing the AccountService at any one time, even if they are in different methods. 这将是线程安全的,因为任何时候只有一个线程将访问AccountService,即使它们使用不同的方法。

If that still isn't sufficient, then you'll need to use more sophisticated locking approaches. 如果仍然不够,那么您将需要使用更复杂的锁定方法。 One common approach would be to lock the accounts individually before you modify them... but then you must be very careful to take the locks in a consistent order (eg by account ID) otherwise you will run into deadlocks. 一种常见的方法是在修改帐户之前单独锁定帐户...但是您必须非常小心地以一致的顺序(例如通过帐户ID)获取锁定,否则您将遇到死锁。

Finally if AccountService is a remote service then you are into distributed locking territory.... unless you have a PhD in computer science and years of research budget to burn you should probably avoid going there. 最后,如果AccountService是一个远程服务,那么你进入分布式锁定领域....除非你拥有计算机科学博士学位和多年的研究预算,你应该避免去那里。

难道你不能避免使用AtomicReference<Double>同步帐户余额,以及get()set()吗?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM