同步值,而不是对象
我想在 Java 中做类似的事情,
public void giveMoney(String userId, int money) {
synchronized (userId) {
Profile p = fetchProfileFromDB(userId);
p.setMoney(p.getMoney() + userId);
saveProfileToDB(p);
}
}
但是当然,在字符串上同步是不正确的。做这样的事情的正确方法是什么?
I want to do something like this in Java
public void giveMoney(String userId, int money) {
synchronized (userId) {
Profile p = fetchProfileFromDB(userId);
p.setMoney(p.getMoney() + userId);
saveProfileToDB(p);
}
}
But of course, synchronizing on a string is not correct. What's a correct way to do something like this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
如果用户 ID 集有限,您可以在
String
的内部版本上进行同步。使用
String.intern()
(它有一些缺点)或类似 GuavaInterners
(如果您需要对实习进行更多控制)。If the set of user ids is limited, you can synchronize on an interned version of the
String
.Either use
String.intern()
(which has a few drawbacks) or something like GuavaInterners
if you need a bit more control over the interning.原则上,您可以在 Java 中的任何对象上进行同步。在
String
对象上进行同步本身并不是“不正确”;这取决于你到底在做什么。但如果
userId
是方法中的局部变量,那么这将不起作用。执行该方法的每个线程都有自己的变量副本(可能为每个线程引用不同的 String 对象);当然,线程之间的同步仅当您使多个线程在同一对象上同步时才有效。您必须在包含具有
synchronized
块的方法的对象的成员变量上创建要同步的对象。如果多个线程随后调用同一对象上的方法,您将实现互斥。您还可以使用 java.util.concurrent.locks 包中的显式锁,如果您需要的话,它可以为您提供更多控制:
特别是如果您想要一个用于写入的独占锁,但又不这样做如果希望线程在读取时必须相互等待,您可能需要使用
ReadWriteLock
。In principle, you can synchronize on any object in Java. It's not in itself "not correct" to synchronize on a
String
object; it depends on what exactly you're doing.But if
userId
is a local variable in a method, then this is not going to work. Each thread that executes the method with have its own copy of the variable (presumably referring to a differentString
object for each thread); synchronizing between threads ofcourse only works when you make multiple threads synchronize on the same object.You'd have to make the object you're synchronizing on a member variable of the object that contains the method in which you have the
synchronized
block. If multiple threads are then calling the method on the same object, you'll achieve mutual exclusivity.You could also use explicit locks from the
java.util.concurrent.locks
package, that can give you more control if you need that:Especially if you want an exclusive lock for writing, but you don't want threads to have to wait for each other when reading, you might want to use a
ReadWriteLock
.我想有几个选择。
最简单的是,您可以将
userId
映射到线程安全映射中的锁对象。其他人提到了实习,但我认为这不是一个可行的选择。然而,更常见的选项是在
p
(配置文件)上同步。如果 getProfile() 是线程安全的,那么这是合适的,并且根据其名称,我怀疑它可能是线程安全的。I guess there are a few options.
The easiest is that you could map a
userId
to a lock object in a threadsafe map. Others have mentioned interning but I don't think that's a viable option.However, the more common option would be to synchronize on
p
(the Profile). This is appropriate ifgetProfile()
is threadsafe, and by its name I would suspect it might be.从理论上讲,由于 interned 对象可以进行 GC,因此可以在不同时间同步不同对象(具有相同值)。互斥性仍然得到保证,因为不可能同时在不同对象上进行同步。
但是,如果我们在不同的对象上进行同步,则发生之前的关系就存在疑问。我们必须检查实施情况才能找到答案。而且由于它涉及 GC,而 Java 内存模型没有解决这一问题,因此推理可能相当困难。
这是理论上的反对意见。实际上我认为这不会造成任何问题。
尽管如此,仍然可以有简单、直接且理论上正确的解决方案来解决您的问题。例如简单的基于Java名称的锁?
Theoretically speaking, since interned objects can be GC-ed, it's possible to synchronized on different objects (of the same value) at different times. Mutual exclusivity is still guaranteed, since it's not possible to synchronized on different objects at the same time.
However, if we synchronized on different objects, the happens-before relation is at doubt. We have to examine the implementation to find out. And since it involves GC, which Java Memory Model does not address, the reasoning can be quite difficult.
That's a theoretical objection; practically I don't think it'll cause any problem.
Still, there can be simple, direct, and theoretically correct solution to your problem. For example Simple Java name based locks?
您可以使用字符串的代理对象。
每当您访问
userId
时,请使用此互斥锁。You can use a proxy object for the string.
Use this mutex whenever you access
userId
.根据您的示例,我假设您想要获取配置文件类的锁,更改它,然后释放锁。我认为同步并不完全是您所需要的。您需要一个类来管理这些记录,并允许您在需要对其进行更改时锁定和解锁记录,即源代码控制样式。
看看这个: 锁定Java 5 类
Based on your example, I assume you want to obtain a lock to a profile class, change it, and then release the lock. Synchronization is not exactly what you need in my opinion. You need a class that manages those records and lets you lock and unlock a record when changes need to be made to it, aka source control style.
Check this out: Lock class Java 5
怎么样:
它很简单,而且显而易见。
What about this:
It's simple and above all obvious.