了解Java中的同步块
我试图理解对象同步的概念。使用 Java Cert Book 中的这个示例,您能否帮助我理解以下两段代码之间的行为差异(一段代码我们在要保护其方法/操作免受竞争条件影响的对象上进行同步,另一段代码我们使用辅助对象作为锁来实现相同的目标):
1.
class Client {
BankAccount account;
public void updateTransaction() {
synchronized (account) {
account.update(); // update is safe because of lock on account obj
}
}
public double withdrawFunds() {
double amt;
synchronized (account) {
account.calculateInterest();
amt= account.withdraw();
}
return amt;
}
}
2.
class Client {
BankAccount account;
Object lock = new Object();
public void updateTransaction() {
synchronized (lock) {
account.update(); // update is safe because of a lock
}
}
public double withdrawFunds() {
double amt;
synchronized (lock) {
account.calculateInterest();
amt= account.withdraw();
}
return amt;
}
}
I am trying to understand the concept of synchronizing on an object. Using this example from the Java Cert Book, can you help me understand the difference in behavior between the following 2 pieces of code (one where we synchronize on the object whose methods/operations we want to protect from the race condition and another where we use a helper object as a lock to achieve the same goal):
1.
class Client {
BankAccount account;
public void updateTransaction() {
synchronized (account) {
account.update(); // update is safe because of lock on account obj
}
}
public double withdrawFunds() {
double amt;
synchronized (account) {
account.calculateInterest();
amt= account.withdraw();
}
return amt;
}
}
2.
class Client {
BankAccount account;
Object lock = new Object();
public void updateTransaction() {
synchronized (lock) {
account.update(); // update is safe because of a lock
}
}
public double withdrawFunds() {
double amt;
synchronized (lock) {
account.calculateInterest();
amt= account.withdraw();
}
return amt;
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
这两个都会抛出 NullPointerExceptions,但只是在不同的地方。
您应该始终在永远不会为空(最终)的不可变引用上进行同步。
我想你的问题的答案是,对于给定的客户端,帐户可能会发生变化(假设有设置者),但锁定永远不会改变。同样,保证这一点的唯一方法是将其标记为最终版本。
Both of those will throw NullPointerExceptions, but only in different places.
You should always synchronize on an immutable reference that will never be null (final).
I guess the answer to your question is that account will potentially change for a given client (assuming there is a setter) but lock never should. Again, the only way to guarantee that is to mark it final.
更好的方法是在可以封装锁定时避免暴露锁定。
A better approach is to avoid exposing locking when it can be encapsulated.
区别在于系统中其他线程正在执行的操作。在第一种情况下,如果有任何原因导致某些其他逻辑(在其他代码中定义)不应与 updateTransaction 方法同时执行,则也可以在帐户对象上同步该逻辑。相反,如果一些不相关的代码在与 updateTransaction 无关的情况下也使用 account 作为锁,则可能会出现假同步。
在第二种情况下,锁对象仅对 Client 类可见,因此可以保证唯一进行的同步是您在此类中指定的同步。
我同意 Peter Lawrey 的观点,最好的方法是将同步逻辑封装在一个有意义的地方,并为此使用私有/受保护的可见性锁定对象。
The difference is with respect to what other threads are doing in your system. In the first case, if there are any reasons why some other logic (defined in some other code) should not be exercised concurrently with the updateTransaction method, that logic could also be synchronized on the account object. Conversely, you might have a false synchronization if some unrelated code also uses account as the lock when it has nothing to do with updateTransaction.
In the second case, the lock object is only visible to the Client class, so you are guaranteed that the only synchronization that goes on is what you have specified in this class.
I agree with Peter Lawrey that the best approach is to encapsulate the synchronization logic in a single place where it makes sense, and use private/protected visibility lock objects for that.
在这两种情况下,没有区别,只是风格上的区别。
如果可以更改帐户,则可能会出现同步错误对象的潜在问题。将该属性确定为最终属性,然后就可以开始了。
In those two cases, there is no difference, but a stylistic difference.
If account could be changed, there is a potential problem that could be synchronizing on the wrong object. Make that property final and you're good to go.
区别在于谁持有锁。
第一种情况更好,因为对帐户的相同引用也可以在其他线程中共享,例如在
Manager
类中,如果该经理需要访问该帐户,应尝试保留在Client
线程尝试同时访问帐户的任何情况下都会锁定自己。在第二段代码中,
Manager
需要对lock
对象进行锁定,以防止对数据造成损坏,因此需要获取帐户和锁。最好的方法是将锁封装在账户内部。
The difference is who holds the Lock.
The first case is the better, because the same reference to account could be in also shared in other thread, for example in the class
Manager
, and if this manager needs to access the account, should try to hold the lock himself in any case that theClient
thread tries to access the account at the same time.In the second code, the
Manager
needs to lock on thelock
object, for preventing doing damage to the data, so its needs to get the account and the lock.The best approach is to encapsulate the lock inside account.
我会使用选项 2,只需将锁定对象设为最终对象即可。帐户对象可能会更改,特别是当客户可以拥有多个帐户时。如果您使用最终锁定对象,您将确保对帐户的任何更改都是同步的,即使帐户对象更改为不同的实例也是如此。
I would use option 2, just make the lock object final. The account object can probably be changed, especially if the client can have more than one account. If you use a final lock object you will ensure that any change to the account is synchronized, even if the account object changes to a different instance.