Java基于参数的同步(名为互斥锁/锁)

2024-05-22

我正在寻找一种根据接收到的参数来同步方法的方法,如下所示:

public synchronized void doSomething(name){
//some code
}

我想要方法doSomething同步基于name参数如下:

线程 1:doSomething("a");

线程 2:doSomething("b");

线程 3:doSomething("c");

线程 4:doSomething("a");

线程 1、线程 2 和线程 3 将在不同步的情况下执行代码,但线程 4 将等待线程 1 完成代码,因为它具有相同的“a”值。

Thanks

UPDATE

根据都铎的解释,我认为我面临另一个问题: 这是新代码的示例:

private HashMap locks=new HashMap();
public void doSomething(String name){
    locks.put(name,new Object());
    synchronized(locks.get(name)) {
        // ...
    }
    locks.remove(name);
}

我不填充锁映射的原因是因为名称可以具有任何值。

根据上面的示例,当多个线程同时从 hashmap 中添加/删除值时,可能会出现该问题,因为 HashMap 不是线程安全的。

所以我的问题是我是否能做到HashMap a ConcurrentHashMap这是线程安全的,同步块会阻止其他线程访问locks.get(name)吗?


TL;DR:

I use 并发引用哈希映射 https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/util/ConcurrentReferenceHashMap.html来自 Spring 框架。请检查下面的代码。


虽然这个帖子很老了,但仍然很有趣。因此,我想分享一下我使用Spring框架的方法。

我们正在尝试实现的称为命名互斥体/锁。正如建议的都铎的回答 https://stackoverflow.com/a/12450424/1814420,这个想法是有一个Map存储锁名称和锁对象。代码如下所示(我从他的答案中复制它):

Map<String, Object> locks = new HashMap<String, Object>();
locks.put("a", new Object());
locks.put("b", new Object());

然而,这种方法有两个缺点:

  1. OP已经指出了第一个:如何同步访问locks哈希映射?
  2. 如何删除一些不再需要的锁?否则,locks哈希图将不断增长。

第一个问题可以通过使用来解决并发哈希映射 https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html。对于第二个问题,我们有两个选择:手动检查并从映射中删除锁,或者以某种方式让垃圾收集器知道哪些锁不再使用,GC 将删除它们。我会选择第二种方式。

当我们使用HashMap, or ConcurrentHashMap,它创建了强引用。要实现上面讨论的解决方案,应该使用弱引用(要了解什么是强/弱引用,请参阅本文 https://community.oracle.com/blogs/enicholas/2006/05/04/understanding-weak-references or 这个帖子 https://stackoverflow.com/questions/9809074/java-difference-between-strong-soft-weak-phantom-reference).


所以,我用并发引用哈希映射 https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/util/ConcurrentReferenceHashMap.html来自 Spring 框架。如文档中所述:

A ConcurrentHashMap对两个键都使用软引用或弱引用 和价值观。

这个类可以用来替代Collections.synchronizedMap(new WeakHashMap<K, Reference<V>>())在 为了在并发访问时支持更好的性能。这 实现遵循相同的设计约束ConcurrentHashMap空值和空键除外 都支持。

这是我的代码。这MutexFactory管理所有的锁<K>是密钥的类型。

@Component
public class MutexFactory<K> {

    private ConcurrentReferenceHashMap<K, Object> map;

    public MutexFactory() {
        this.map = new ConcurrentReferenceHashMap<>();
    }

    public Object getMutex(K key) {
        return this.map.compute(key, (k, v) -> v == null ? new Object() : v);
    }
}

Usage:

@Autowired
private MutexFactory<String> mutexFactory;

public void doSomething(String name){
    synchronized(mutexFactory.getMutex(name)) {
        // ...
    }
}

单元测试(该测试使用等待性 https://github.com/awaitility/awaitility某些方法的库,例如await(), atMost(), until()):

public class MutexFactoryTests {
    private final int THREAD_COUNT = 16;

    @Test
    public void singleKeyTest() {
        MutexFactory<String> mutexFactory = new MutexFactory<>();
        String id = UUID.randomUUID().toString();
        final int[] count = {0};

        IntStream.range(0, THREAD_COUNT)
                .parallel()
                .forEach(i -> {
                    synchronized (mutexFactory.getMutex(id)) {
                        count[0]++;
                    }
                });
        await().atMost(5, TimeUnit.SECONDS)
                .until(() -> count[0] == THREAD_COUNT);
        Assert.assertEquals(count[0], THREAD_COUNT);
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java基于参数的同步(名为互斥锁/锁) 的相关文章

随机推荐