同步值,而不是对象

发布于 2024-12-06 10:16:33 字数 304 浏览 0 评论 0原文

我想在 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

暖风昔人 2024-12-13 10:16:33

如果用户 ID 集有限,您可以在 String 的内部版本上进行同步。

使用 String.intern() (它有一些缺点)或类似 Guava Interners(如果您需要对实习进行更多控制)。

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 Guava Interners if you need a bit more control over the interning.

羞稚 2024-12-13 10:16:33

原则上,您可以在 Java 中的任何对象上进行同步。在 String 对象上进行同步本身并不是“不正确”;这取决于你到底在做什么。

但如果 userId 是方法中的局部变量,那么这将不起作用。执行该方法的每个线程都有自己的变量副本(可能为每个线程引用不同的 String 对象);当然,线程之间的同步仅当您使多个线程在同一对象上同步时才有效。

您必须在包含具有 synchronized 块的方法的对象的成员变量上创建要同步的对象。如果多个线程随后调用同一对象上的方法,您将实现互斥。

class Something {
    private Object lock = new Object();

    public void someMethod() {
        synchronized (lock) {
            // ...
        }
    }
}

您还可以使用 java.util.concurrent.locks 包中的显式锁,如果您需要的话,它可以为您提供更多控制:

class Something {
    private Lock lock = new ReentrantLock();

    public void someMethod() {
        lock.lock();
        try {
            // ...
        } finally {
            lock.unlock();
        }
    }
}

特别是如果您想要一个用于写入的独占锁,但又不这样做如果希望线程在读取时必须相互等待,您可能需要使用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 different String 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.

class Something {
    private Object lock = new Object();

    public void someMethod() {
        synchronized (lock) {
            // ...
        }
    }
}

You could also use explicit locks from the java.util.concurrent.locks package, that can give you more control if you need that:

class Something {
    private Lock lock = new ReentrantLock();

    public void someMethod() {
        lock.lock();
        try {
            // ...
        } finally {
            lock.unlock();
        }
    }
}

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.

太阳男子 2024-12-13 10:16:33

我想有几个选择。

最简单的是,您可以将 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 if getProfile() is threadsafe, and by its name I would suspect it might be.

爱已欠费 2024-12-13 10:16:33

从理论上讲,由于 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?

噩梦成真你也成魔 2024-12-13 10:16:33

您可以使用字符串的代理对象。

Object userIdMutex = new Object();

synchronized (userIdMutex) {
    Profile p = getProfile(userId);
    p.setMoney(p.getMoney() + p);
    saveProfile(p);
}

每当您访问 userId 时,请使用此互斥锁。

You can use a proxy object for the string.

Object userIdMutex = new Object();

synchronized (userIdMutex) {
    Profile p = getProfile(userId);
    p.setMoney(p.getMoney() + p);
    saveProfile(p);
}

Use this mutex whenever you access userId.

ˇ宁静的妩媚 2024-12-13 10:16:33

根据您的示例,我假设您想要获取配置文件类的锁,更改它,然后释放锁。我认为同步并不完全是您所需要的。您需要一个类来管理这些记录,并允许您在需要对其进行更改时锁定和解锁记录,即源代码控制样式。

看看这个: 锁定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

时光暖心i 2024-12-13 10:16:33

怎么样:

String userId = ...;
Object userIdLock = new Object();
synchronized (userIdLock) {
    Profile p = getProfile(userId);
    p.setMoney(p.getMoney() + p);
    saveProfile(p);
}

它很简单,而且显而易见

What about this:

String userId = ...;
Object userIdLock = new Object();
synchronized (userIdLock) {
    Profile p = getProfile(userId);
    p.setMoney(p.getMoney() + p);
    saveProfile(p);
}

It's simple and above all obvious.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文