当前位置: 首页 > 知识库问答 >
问题:

按值而不是按对象同步

邢璞
2023-03-14

下面是一个实现:

import java.util.*;

public class Mutex<T> {

    private final List<T> list = new ArrayList();

    public synchronized Lock acquireLock(
            T value
            ) throws InterruptedException {
        if(list.contains(value)) {
            while(list.contains(value)) {
                this.wait();
            }
        } else {
            list.add(value);
        }
        return new Lock(value);
    }

    public class Lock {

        private final T value;

        public Lock(T value) {
            this.value = value;
        }

        public T getValue() {
            return value;
        }

        public void release() {
            synchronized(Mutex.this) {
                list.remove(value);
                Mutex.this.notifyAll();
            }
        }
    }
}

下面是一个示例用法来检查可操作性:

public class Test {

    private Mutex mutex = new Mutex();

    public static void main(String[] args) {
        Test test = new Test();
        Thread t1 = new Thread(() -> {
            try {
                test.test("SameValue");
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        });
        t1.setName("Thread 1");
        Thread t2 = new Thread(() -> {
            try {
                test.test("SameValue");
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        });
        t2.setName("Thread 2");

        t1.start();
        t2.start();
    }

    public void test(String value)
            throws
            InterruptedException {
        Lock lock = mutex.acquireLock(value);
        try {
            Thread.sleep(5000);
            System.out.println(Thread.currentThread().getName());
        } finally {
            lock.release();
        }
    }
}

共有1个答案

笪烨
2023-03-14

关于你们的执行,

我会使用Set而不是List来保存值(我假设这些值有适当的equals/hashcode,这样才有意义):List#contains方法在O(n)中,如果同时使用大量IBAN,这可能会很昂贵。

另外,您应该避免使用synchronize(this)(与synchronized关键字on方法相同)。

例如:

  1. 线程1获得v1的锁
  2. 线程2为V1->块请求锁,直到线程1释放锁
  3. 线程3为V2->请求锁,因为线程2阻塞。

为了解决您的问题,我使用如下内容:

import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Locks<T> {

    private final Lock lock = new ReentrantLock();

    //a Bimap from guava might be better here if you have the dependency
    //in your project
    private final Map<Reference<?>, T> valuePerReference = new HashMap<>();
    private final Map<T, Reference<Lock>> locks = new HashMap<>();

    private final ReferenceQueue<Lock> lockReferenceQueue = new ReferenceQueue<>();

    public Locks() {
        final Thread cleanerThread = new Thread(new Cleaner());
        cleanerThread.setDaemon(true);
        cleanerThread.start();
    }

    /**
     * @param value the value the synchronization must be made on
     * @return a lock that can be used to synchronize block of code.
     */
    public Lock getLock(T value) {
        lock.lock();
        try {
            return getExistingLock(value).orElseGet(() -> createNewLock(value));
        } finally {
            lock.unlock();
        }
    }


    private Optional<Lock> getExistingLock(T value) {
        return Optional.ofNullable(locks.get(value)).map(Reference::get);
    }

    private Lock createNewLock(T value) {
        //I create ReentrantLock here but a Supplier<Lock> could be a parameter of this
        //class to make it more generic. Same remark for SoftReference below.
        final Lock lock = new ReentrantLock();
        final Reference<Lock> reference = new SoftReference<>(lock, lockReferenceQueue);
        this.locks.put(value,reference);
        this.valuePerReference.put(reference,value);
        return lock;
    }


    private void removeLock(Reference<?> reference) {
        lock.lock();
        try {
            final T value = valuePerReference.remove(reference);
            locks.remove(value);
        } finally {
            lock.unlock();
        }
    }


    private class Cleaner implements Runnable {
        @Override
        public void run() {
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    final Reference<? extends Lock> garbaged = lockReferenceQueue.remove();
                    removeLock(garbaged);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }
    }

}
import java.util.concurrent.locks.Lock;

public class Usage {

    private final Locks<String> locks = new Locks<>();


    public void doSomethind(String iban) {
        final Lock lock = locks.getLock(iban);
        lock.lock();
        try {
            //.. do something with your iban
        } finally {
            lock.unlock();
        }
    }
}
 类似资料:
  • 因为普罗米修斯topk返回的结果比预期的要多,而且因为https://github.com/prometheus/prometheus/issues/586需要客户端处理,而这些处理尚未通过https://github.com/grafana/grafana/issues/7664提供,所以我正在尝试对我的类似问题进行不同的近期解决方案。 在我的特殊情况下,我想要绘制的大多数度量值在大多数情况下都

  • 问题内容: 经过一些帮助MSChart-强制从Origin绘制折线图]后,我设法将以下MSSQL查询放在一起,以用于折线图。 但是,我有一个问题。该查询返回类似以下内容的内容: 这意味着我每天都在阅读,而实际上每个不同的值仅需要一行。在每种情况下,我都希望它第一次出现,因此对于上面的示例,我希望查询返回: 我看了看似是基于类似前提的几个问题,但是所有答复都是针对特定情况而量身定制的(有些甚至使用L

  • 问题内容: 我有一个在while循环中生成一堆数据的python脚本。我需要将此数据写入CSV文件,因此它是按列而不是行写入的。 例如,在脚本的循环1中,我生成: 我需要它来反映在我的csv脚本中,如下所示: 在第二个循环中,我生成: 我需要它来像这样在我的csv文件中查找: 依此类推,直到while循环结束。有谁能够帮助我? 编辑 while循环可以持续超过100,000个循环 问题答案: 之所

  • 问题内容: 我想使用abs()对元组进行排序,而无需实际将元组的元素更改为正值。 根据python Wiki(https://wiki.python.org/moin/HowTo/Sorting/#Key_Functions),sorted(list,key =)函数假定使用参数 key 进行排序,而实际上并未更改清单。但是,abs()只需要int(),如果我需要这样做,我还没有想办法将元组变成i

  • 我正在尝试使用创建一个6乘12的矩阵 虽然这样可以工作,但问题是内部数组实际上都引用了相同的对象。 我希望(并且期望)的值为。 如何强制填充给定参数的按值复制(例如:是我的例子中的参数)而不是按引用复制?

  • 问题内容: 让我们举个例子 我想在列表中附加值,但列表的值也已更改。 我想我不知道为什么会这样(python通过引用传递列表)。 我的问题是“如何通过值传递它,以使添加不会更改中的值?” 问题答案: