Java同步取决于方法参数

问题描述 投票:1回答:4

如何在方法参数值上提供同步?

应同步使用“相同”参数值A的所有方法调用。具有不同参数值的方法调用,例如即使有A的呼叫已经在等待,B也可以访问。 B的下一个并发呼叫也必须等待第一个B被释放。

我的用例:我希望在ID级别上同步对JPA实体的访问,但是希望避免悲观锁定,因为我需要一种队列。用于锁定的“密钥”旨在作为实体ID - 实际上是Java Long类型。

protected void entityLockedAccess(SomeEntity myEntity) {
    //getId() returns different Long objects so the lock does not work
    synchronized (myEntity.getId()) {
        //the critical section ...
    }
}

我读到了锁定对象,但我不确定它们在我的情况下是否适合。在顶层,我想管理对我的应用程序执行关键代码的特定REST调用。

谢谢,克里斯

java rest synchronization synchronized
4个回答
5
投票

据我所知,你基本上想要为每个SomeEntity ID提供一个不同的,独特的锁。

你可以用Map<Integer, Object>意识到这一点。

您只需将每个ID映射到一个对象。如果已有对象,则重用它。这看起来像这样:

static Map<Integer, Object> locks = new ConcurrentHashMap<>();

public static void main(String[] args)
{
    int i1 = 1;
    int i2 = 2;

    foo(i1);
    foo(i1);
    foo(i2);
}

public static void foo(int o)
{
    synchronized (locks.computeIfAbsent(o, k -> new Object()))
    {
        // computation
    }
}

这将在地图中创建2个锁定对象,因为i1的对象在第二个foo(i1)调用中被重用。


1
投票

问题是你根本不应该同步值(例如字符串或Integer对象)。

含义:你需要在这里定义一些特殊的EntityId类,当然,所有使用相同ID的“数据”都需要使用相同的EntityId对象。


1
投票

汇集和可能重用的对象不应用于同步。如果是,则可能导致无关的线程因无用的堆栈跟踪而死锁。

具体来说,String文字和盒装基元(如Integers)不应该用作锁定对象,因为它们被合并并重复使用。

Boolean对象的情况更糟,因为BooleanBoolean.TRUEBoolean.FALSE只有两个实例,每个使用布尔值的类都将指代其中一个。

我读到了锁定对象,但我不确定它们在我的情况下是否适合。在顶层,我想管理对我的应用程序执行关键代码的特定REST调用。

您的数据库将负责并发写入和其他事务问题。您所需要做的就是使用交易。

我还建议你去解决经典问题(DIRTY READs NON Repeatable reads)。您也可以使用Optimistic Locking


0
投票
    private static final Set<Integer> lockedIds = new HashSet<>();

    private void lock(Integer id) throws InterruptedException {
        synchronized (lockedIds) {
            while (!lockedIds.add(id)) {
                lockedIds.wait();
            }
        }
    }

    private void unlock(Integer id) {
        synchronized (lockedIds) {
            lockedIds.remove(id);
            lockedIds.notifyAll();
        }
    }

    public void entityLockedAccess(SomeEntity myEntity) throws InterruptedException {
        try {
            lock(myEntity.getId());

            //Put your code here.
            //For different ids it is executed in parallel.
            //For equal ids it is executed synchronously.

        } finally {
            unlock(myEntity.getId());
        }
    }
  • id不仅可以是'Integer',还可以是任何具有正确覆盖'equals'和'hashCode'方法的类。
  • try-finally - 非常重要 - 即使您的操作引发异常,您也必须保证在操作后解锁等待的线程。
  • 如果您的后端分布在多个服务器/ JVM上,它将无法工作。
© www.soinside.com 2019 - 2024. All rights reserved.