简体   繁体   English

互斥方法

[英]Mutually exclusive methods

I am on my way learning Java multithread programming. 我正在学习Java多线程编程。 I have a following logic: 我有以下逻辑:

Suppose I have a class A 假设我有A班

class A {
    ConcurrentMap<K, V> map;

    public void someMethod1 () {
        // operation 1 on map
        // operation 2 on map
    }

    public void someMethod2 () {
        // operation 3 on map
        // operation 4 on map
    }
}

Now I don't need synchronization of the operations in "someMethod1" or "someMethod2". 现在,我不需要同步“ someMethod1”或“ someMethod2”中的操作。 This means if there are two threads calling "someMethod1" at the same time, I don't need to serialize these operations (because the ConcurrentMap will do the job). 这意味着,如果有两个线程同时调用“ someMethod1”,则无需序列化这些操作(因为ConcurrentMap将完成此工作)。

But I hope "someMethod1" and "someMethod2" are mutex of each other, which means when some thread is executing "someMethod1", another thread should wait to enter "someMethod2" (but another thread should be allowed to enter "someMethod1"). 但是我希望“ someMethod1”和“ someMethod2”彼此互斥,这意味着当某个线程正在执行“ someMethod1”时,另一个线程应等待输入“ someMethod2”(但应允许另一个线程输入“ someMethod1”)。

So, in short, is there a way that I can make "someMethod1" and "someMethod2" not mutex of themselves but mutex of each other? 简而言之,有没有一种方法可以使“ someMethod1”和“ someMethod2”不是互斥体,而是互斥体?

I hope I stated my question clear enough... 我希望我的问题已经足够清楚了...

Thanks! 谢谢!

I tried a couple attempts with higher-level constructs, but nothing quite came to mind. 我尝试了几次使用更高级别的构造的尝试,但没有想到。 I think this may be an occasion to drop down to the low level APIs: 我认为这可能是一个使用低级API的机会:

EDIT: I actually think you're trying to set up a problem which is inherently tricky (see second to last paragraph) and probably not needed (see last paragraph). 编辑:我实际上认为您正在尝试设置一个固有的棘手问题(请参阅第二至第二段),并且可能不需要(请参阅最后一段)。 But that said, here's how it could be done, and I'll leave the color commentary for the end of this answer. 但这就是说,这是可以完成的方式,我将在此答案的结尾处留下颜色评论。

private int someMethod1Invocations = 0;
private int someMethod2Invocations = 0;

public void someMethod1() {
    synchronized(this) {
        // Wait for there to be no someMethod2 invocations -- but
        // don't wait on any someMethod1 invocations.
        // Once all someMethod2s are done, increment someMethod1Invocations
        // to signify that we're running, and proceed
        while (someMethod2Invocations > 0)
            wait();
        someMethod1Invocations++;
    }

    // your code here

    synchronized (this) {
        // We're done with this method, so decrement someMethod1Invocations
        // and wake up any threads that were waiting for that to hit 0.
        someMethod1Invocations--;
        notifyAll();
    }
}

public void someMethod2() {
    // comments are all ditto the above
    synchronized(this) {
        while (someMethod1Invocations > 0)
            wait();
        someMethod2Invocations++;
    }

    // your code here
    synchronized(this) {
        someMethod2Invocations--;
        notifyAll();
    }
}

One glaring problem with the above is that it can lead to thread starvation . 上面的一个明显问题是它可能导致线程饥饿 For instance, someMethod1() is running (and blocking someMethod2() s), and just as it's about to finish, another thread comes along and invokes someMethod1() . 例如, someMethod1()正在运行(并阻塞someMethod2() ),并且在即将完成时,另一个线程出现并调用someMethod1() That proceeds just fine, and just as it finishes another thread starts someMethod1() , and so on. 这样就可以了,就在它完成另一个线程时,启动someMethod1() ,依此类推。 In this scenario, someMethod2() will never get a chance to run. 在这种情况下, someMethod2()将永远无法运行。 That's actually not directly a bug in the above code; 实际上,这不是上面代码中的直接错误。 it's a problem with your very design needs, one which a good solution should actively work to solve. 这是您非常需要设计的问题,一个好的解决方案应该积极地解决这个问题。 I think a fair AbstractQueuedSynchronizer could do the trick, though that is an exercise left to the reader. 我认为一个公平的AbstractQueuedSynchronizer可以解决问题,尽管这是留给读者的练习。 :) :)

Finally, I can't resist but to interject an opinion: given that ConcurrentHashMap operations are pretty darn quick, you could be better off just putting a single mutex around both methods and just being done with it. 最后,我忍不住插话:鉴于ConcurrentHashMap操作非常快,您最好将两种方法都放在一个互斥体上并用它完成。 So yes, threads will have to queue up to invoke someMethod1() , but each thread will finish its turn (and thus let other threads proceed) extremely quickly. 因此,是的,线程将必须排队以调用someMethod1() ,但是每个线程将非常快速地完成其轮换(并因此让其他线程继续进行)。 It shouldn't be a problem. 这不应该是一个问题。

This probably can't work (see comments) - leaving it for information. 这可能行不通 (请参阅评论)-供参考。


One way would be to use Semaphores : 一种方法是使用信号量

  • one semaphore sem1 , with one permit, linked to method1 一个信号量sem1 (具有一个许可),链接到method1
  • one semaphore sem2 , with one permit, linked to method2 一个信号量sem2 (具有一个许可),链接到method2

when entering method1, try to acquire sem2's permit, and if available release it immediately. 进入method1时,尝试获取sem2的许可证,如果有,请立即将其释放。

See this post for an implementation example. 有关实现示例,请参见本文

Note: in your code, even if ConcurrentMap is thread safe, operation 1 and operation 2 (for example) are not atomic - so it is possible in your scenario to have the following interleaving: 注意:在您的代码中,即使ConcurrentMap是线程安全的,操作1和操作2(例如)也不是原子的-因此,在您的方案中可能会有以下交错:

  • Thread 1 runs operation 1 线程1运行操作1
  • Thread 2 runs operation 1 线程2运行操作1
  • Thread 2 runs operation 2 线程2运行操作2
  • Thread 1 runs operation 2 线程1运行操作2

I think this should work 我认为这应该工作

class A {
    Lock lock = new Lock();

    private static class Lock {
        int m1;
        int m2;
    }

    public void someMethod1() throws InterruptedException {
        synchronized (lock) {
            while (lock.m2 > 0) {
                lock.wait();
            }
            lock.m1++;
        }

        // someMethod1 and someMethod2 cannot be here simultaneously

        synchronized (lock) {
            lock.m1--;
            lock.notifyAll();
        }
    }

    public void someMethod2() throws InterruptedException {
        synchronized (lock) {
            while (lock.m1 > 0) {
                lock.wait();
            }
            lock.m2++;
        }

        // someMethod1 and someMethod2 cannot be here simultaneously

        synchronized (lock) {
            lock.m2--;
            lock.notifyAll();
        }
    }
}

First of all : Your map is thread safe as its ConcurrentMap. 首先 :您的地图作为ConcurrentMap是线程安全的。 This means that operations on this map like add,contains etc are thread safe. 这意味着此地图上的操作(例如add,contains等)是线程安全的。

Secondaly This doesn't guarantee that even your methods (somemethod1 and somemethod2) are also thread safe. Secondaly这并不能保证,即使你的方法(somemethod1和somemethod2)也线程安全的。 So your methods are not mutually exclusive and two threads at same time can access them. 因此,您的方法不是互斥的,并且两个线程可以同时访问它们。

Now you want these to be mutex of each other : One approach could be put all operations (operaton 1,..operation 4) in a single method and based on condition call each. 现在,您希望它们彼此互斥 :一种方法可以将所有操作(操作1,..操作4)放在一个方法中,并基于每个条件调用。

I think you cannot do this without a custom synchronizer. 我认为如果没有自定义同步器,您将无法执行此操作。 I've whipped up this, I called it TrafficLight since it allows threads with a particular state to pass while halting others, until it changes state: 我将其TrafficLight因为它允许具有特定状态的线程通过而同时暂停其他线程,直到改变状态:

public class TrafficLight<T> {

    private final int maxSequence;
    private final ReentrantLock lock = new ReentrantLock(true);
    private final Condition allClear = lock.newCondition();
    private int registered;
    private int leftInSequence;
    private T openState;

    public TrafficLight(int maxSequence) {
        this.maxSequence = maxSequence;
    }

    public void acquire(T state) throws InterruptedException {
        lock.lock();
        try {
            while ((this.openState != null && !this.openState.equals(state)) || leftInSequence == maxSequence) {
                allClear.await();
            }
            if (this.openState == null) {
                this.openState = state;
            }
            registered++;
            leftInSequence++;
        } finally {
            lock.unlock();
        }
    }

    public void release() {
        lock.lock();
        try {
            registered--;
            if (registered == 0) {
                openState = null;
                leftInSequence = 0;
                allClear.signalAll();
            }
        } finally {
            lock.unlock();
        }
    }
}

acquire() will block if another state is active, until it becomes inactive. 如果另一个状态为活动状态,则acquire()将阻塞,直到变为非活动状态为止。

The maxSequence is there to help prevent thread starvation, allowing only a maximum number of threads to pass in sequence (then they'll have to queue like the others). maxSequence可以帮助防止线程匮乏,仅允许最大数量的线程按顺序传递(然后它们必须像其他线程一样排队)。 You could make a variant that uses a time window instead. 您可以制作一个使用时间窗口的变体。

For your problem someMethod1() and someMethod2() would call acquire() with a different state each at the start, and release() at the end. 对于您的问题, someMethod1()someMethod2()会在开始时分别调用带有不同状态的acquire(),并在结尾处调用release()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM