【问题标题】:Java: Synchronization based on object valueJava:基于对象值的同步
【发布时间】:2021-03-20 12:46:38
【问题描述】:

我想根据输入参数同步一个方法或一个块。

所以我有一个 API,它在后有效负载中具有两个长类型(可以是原始或包装)输入(比如说 id1 和 id2),可以是 JSON。该API会被多个线程同时或不同时间随机调用。

现在如果第一个 API 调用的 id1=1 和 id2=1,同时另一个 API 调用的 id1=1 和 id2=1,它应该等待第一个 API 调用完成处理后再执行第二个称呼。如果第二个 API 调用具有不同的值组合,例如 id1=1 和 id2=2,则它应该通过并行处理而无需任何等待时间。

我不介意创建 API 资源方法也可以调用的服务方法,而不是直接在 API 资源方法处处理。

我正在使用 Spring Boot Rest Controller API。

**编辑** 我已经尝试按照建议使用地图,但这部分有效。它等待所有输入值,而不仅仅是相同的输入值。下面是我的代码:
public static void main(String[] args) throws Exception {
    ApplicationContext context = SpringApplication.run(Application.class, args);
    AccountResource ar = context.getBean(AccountResource.class);
    UID uid1 = new UID();
    uid1.setFieldId(1);
    uid1.setLetterFieldId(1);
    UID uid2 = new UID();
    uid2.setFieldId(2);
    uid2.setLetterFieldId(2);
    UID uid3 = new UID();
    uid3.setFieldId(1);
    uid3.setLetterFieldId(1);
    Runnable r1 = new Runnable() {

        @Override
        public void run() {
            while (true) {
                ar.test(uid1);
            }

        }
    };
    Runnable r2 = new Runnable() {

        @Override
        public void run() {
            while (true) {
                ar.test(uid2);
            }

        }
    };
    Runnable r3 = new Runnable() {

        @Override
        public void run() {
            while (true) {
                ar.test(uid3);
            }

        }
    };
    Thread t1 = new Thread(r1);
    t1.start();
    Thread t2 = new Thread(r2);
    t2.start();
    Thread t3 = new Thread(r3);
    t3.start();

}
@Path("v1/account")
@Service
public class AccountResource {
public void test(UID uid) {
        uidFieldValidator.setUid(uid);
        Object lock;
        synchronized (map) {
            lock = map.get(uid);
            if (lock == null) {
                map.put(uid, (lock = new Object()));
            }
            synchronized (lock) {
                //some operation
            }
        }
    }
}

package com.urman.hibernate.test;

import java.util.Objects;

public class UID {
    
    private long letterFieldId;
    private long fieldId;
    private String value;
    public long getLetterFieldId() {
        return letterFieldId;
    }
    public void setLetterFieldId(long letterFieldId) {
        this.letterFieldId = letterFieldId;
    }
    public long getFieldId() {
        return fieldId;
    }
    public void setFieldId(long fieldId) {
        this.fieldId = fieldId;
    }
    public String getValue() {
        return value;
    }
    public void setValue(String value) {
        this.value = value;
    }
    @Override
    public int hashCode() {
        return Objects.hash(fieldId, letterFieldId);
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        UID other = (UID) obj;
        return fieldId == other.fieldId && letterFieldId == other.letterFieldId;
    }
}

【问题讨论】:

  • 您在地图上和锁定对象上同步。看看你的同步块的范围与我的比较。

标签: java multithreading spring-boot synchronization thread-safety


【解决方案1】:

您需要一组锁,您可以将其保存在地图中并根据需要进行分配。这里我假设你的 id1 和 id2 是字符串;适当调整。

 Map<String,Object> lockMap = new HashMap<>();
   :

 void someMethod(String id1, String id2) {
     Object lock;
     synchronized (lockMap) {
         lock = lockMap.get(id1+id2);
         if (lock == null) lockMap.put(id1+id2, (lock = new Object()));
     }
     synchronized (lock) {
         :
     }
 }

地图操作需要一点“全局”同步,或者您可以使用其中一种并发实现。为了简单起见,我使用了基本的 HashMap。

选择锁后,在其上同步。

【讨论】:

  • 我已经尝试过了,我已经更新了我的问题,请检查。
  • 那是因为你在地图和锁对象上同步了。看看你的同步块的范围与我的比较。
  • lock = lockMap.get(id1+id2); if (lock == null) lockMap.put(id1+id2, (lock = new Object()));您可以使用 computeifabsert 减少到一行
  • @user15187356 ,所以这实际上工作正常。问题出在我使用的地图键上。我的 UID 类的 hashCode 和 Equals 方法似乎很好,但不知何故,它在映射中存储了重复的键,因此每个键都存储了不同的对象,因为该锁对于所述对象不能完美地工作。这个带有 HashMap 键的实际上是一个奇怪的问题。所以到目前为止我所做的是(它工作正常)我使用了 的映射,其中键字符串将类似于 letterfieldId+"`"+fieldId
  • @dreamcrash,谢谢你的建议,我已经改了。
猜你喜欢
  • 2011-12-12
  • 1970-01-01
  • 1970-01-01
  • 2012-07-09
  • 1970-01-01
  • 1970-01-01
  • 2013-04-25
  • 2016-05-08
  • 2015-03-13
相关资源
最近更新 更多