我正在学习Java多线程编程。我有以下逻辑:
假设我有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
}
}
现在,我不需要同步“ someMethod1”或“ someMethod2”中的操作。这意味着,如果有两个线程同时调用“ someMethod1”,则不需要序列化这些操作(因为ConcurrentMap将完成此工作)。
但是我希望“ someMethod1”和“ someMethod2”彼此互斥,这意味着当某个线程正在执行“ someMethod1”时,另一个线程应等待输入“ someMethod2”(但应允许另一个线程输入“ someMethod1”)。
简而言之,有没有一种方法可以使“ someMethod1”和“ someMethod2”不是互斥体,而是互斥体?
我希望我说的问题足够清楚...
谢谢!
最佳答案
我尝试了几次使用更高级别的构造的尝试,但没有想到。我认为这可能是一个使用低级API的机会:
编辑:我实际上认为您正在尝试设置一个固有的棘手问题(请参阅第二至第二段),并且可能不需要(请参阅最后一段)。但这就是说,这是可以完成的方式,我将在此答案的结尾处留下颜色评论。
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();
}
}
上面的一个明显问题是它可能导致thread starvation。例如,
someMethod1()
正在运行(并阻止someMethod2()
),正好即将完成时,另一个线程出现并调用someMethod1()
。这样就可以进行了,就在结束时,另一个线程从someMethod1()
开始,依此类推。在这种情况下,someMethod2()
将永远不会有运行的机会。实际上,这并不是上面代码中的直接错误。这是您非常需要的设计问题,一个好的解决方案应该积极地解决这个问题。我认为一个公平的AbstractQueuedSynchronizer可以解决问题,尽管这是留给读者的练习。 :)最后,我禁不住发表意见:鉴于
ConcurrentHashMap
操作相当快,因此最好将两种方法都放在一个互斥体上,然后使用它来完成。因此,是的,线程将必须排队等待调用someMethod1()
,但是每个线程将非常快速地完成转弯(从而使其他线程继续进行)。这不应该是一个问题。