【问题标题】:Synchronization of methods of differents classes不同类的方法同步
【发布时间】:2018-04-10 16:34:17
【问题描述】:

我有两个类,每个类都有一个方法。

class A {
    private void insert(String usedId){
        // ...
    }
}

class B {
    private void refresh(String userId){
        // ...
    }
}

每个方法都从不同的Thread 调用。它们是从不同的Threads 调用不同的userIds。

当调用第二个方法时,我需要锁定第一个方法,反之亦然,同样的userId

持有List 的ID 并设置锁定是最佳选择吗?

【问题讨论】:

  • 没有。指定用例的最佳选择,而不是指定要做什么。这听起来很像 XY 问题。例如 - 如果两种方法都更新地图,则地图可以设为线程安全等。退后一步 - 你的用例是什么?
  • 有很多方法可以做到这一点,但首先我可能会质疑为什么两个看似独立的类需要锁定同一个资源。
  • 我有用户的API调用修改和定期更新的API调用。我需要在同时调用时对调用进行序列化,并在修改和定期更新之间添加延迟。

标签: java multithreading java-threads


【解决方案1】:

我们介绍了LockDispenser。您将此对象传递给您想要拥有thread safe 的所有As 和Bs。它将为Lock对象提供createLock(String forId),使用后需要通过调用releaseLock(String forId)释放。

public class LockDispenser {
    private final Map<String, Lock> dispenser = new LinkedHashMap<>();

    public Object createLock(String forId) {
        synchronized (dispenser) {
            if (!dispenser.containsKey(forId)) {
                dispenser.put(forId, new Lock());
            }
            Lock lock = dispenser.get(forId);
            lock.referenceCounter++;
            return lock;
        }
    }

    public void releaseLock(String forId) {
        synchronized (dispenser) {
            Lock lock = dispenser.get(forId);
            lock.referenceCounter--;
            if (lock.referenceCounter == 0) {
                dispenser.remove(forId);
            }
        }
    }

    public static class Lock {
        private int referenceCounter = 0;
    }
}

现在真正的线程安全来自于在synchronized 块中使用Lock

public class  A {
    private LockDispenser dispenser;

    public A(LockDispenser dispenser) {
        this.dispenser = dispenser;
    }

    private void insert(String userId) {
        synchronized (dispenser.createLock(userId)) {
            // code
        }
        dispenser.releaseLock(userId); // consider putting this in a finally block
    }
}

public class B {
    private LockDispenser dispenser;

    public B(LockDispenser dispenser) {
        this.dispenser = dispenser;
    }

    private void refresh(String userId) {
        synchronized (dispenser.createLock(userId)) {
            // code
        }
        dispenser.releaseLock(userId); // consider putting this in a finally block
    }
}

确保即使抛出异常也调用releaseLock(String forId)。您可以通过将其放入 finally 块中来做到这一点。

然后像这样创建它们:

public static void main(String... args) {
    LockDispenser fooLock = new LockDispenser();
    A fooA = new A(fooLock);
    B fooB = new B(fooLock);


    LockDispenser barLock = new LockDispenser();
    A barA = new A(barLock);
    B barB = new B(barLock);
}

fooAfooB 彼此是线程安全的,barAbarB 也是如此。

【讨论】:

  • 您将需要使用finally 来释放锁,否则当发生错误时,锁将悬空。
  • 1. lock.referenceCounter 是什么?我认为您指的不是java.util.concurrent.locks.Lock,因为在这种情况下没有特别的理由重新发明轮子2。为什么不使用ConcurrentHashMap.merge 而不是在非线程安全的映射上同步?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-21
  • 2013-08-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多