我不明白这段代码有什么问题。有时两个线程开始执行try块。我每次调用函数时都会创建一个popo
的新实例。可以请任何人弄清问题是什么?
public class Instance {
private static AtomicInteger i = new AtomicInteger(0);
public synchronized void incrementInstance() {
i.getAndIncrement();
}
public synchronized void decrementInstance() {
i.getAndDecrement();
}
public synchronized int getInstances() {
return i.get();
}
}
public class popo {
private static volatile MyMutex instanceMutex = new MyMutex();
public void doSomething() {
synchronized (instanceMutex) {
final Instance no = new Instance();
if (no.getInstances() > 0) {
instanceMutex.wait();
} else {
no.incrementInstance();
}
}
try {
// do something
} finally {
synchronized (instanceMutex) {
final Instance no = new Instance();
if (no.Instances() > 0) {
no.decrementInstance();
}
instanceMutex.notify();
}
}
}
private static class MyMutex {}
}
答案 0 :(得分:1)
try块不在synchronized块内。操作系统可以暂停一个线程并在任何时候恢复另一个线程,如果一个线程刚刚离开同步块并暂停,则另一个线程可以在第一个线程完成同步块之前或同时逻辑地执行try块。 / p>
如果一次只需要一个线程来执行try块,请尝试以下操作:
synchronized (instanceMutex) {
final Instance no = new Instance();
if (no.getInstances() > 0) {
instanceMutex.wait();
} else {
no.incrementInstance();
}
try {
// do something
} finally {
if (no.Instances() > 0) {
no.decementInstance();
}
instanceMutex.notify();
}
}
答案 1 :(得分:1)
问题是,每次拨打no
时,您都在创建doSomething()
的新实例。
synchronized (instanceMutex) {
// each thread will make its own instance here
final Instance no = new Instance();
相反,您需要全局和静态地声明Instance no
,就像MyMutex
一样。
final
只表示no
的引用无法更改(即,您无法在程序的其他位置编写no = new Instance()
)。
static
是您要查找的关键字 - 这意味着每个帖子只会引用Instance no
的单个实例。
如果这没有帮助,如果您有多个线程同时等待,您可能会立即释放所有线程。为了防止这种情况发生,您需要在等待之后循环返回并检查no.getInstances()
,如下所示:
synchronized (instanceMutex) {
final Instance no = new Instance();
while (no.getInstances() > 0) { // this check will need to be synchronized as well
instanceMutex.wait();
}
// and only increment `no` once you've made a successful check.
no.incrementInstance();
}
道德:竞争条件很棘手!
答案 2 :(得分:1)
我认为代码闻到了所有地方:-)实际上,做一些事情,比如创建新的实例,除了访问静态字段之外什么都不会让事情变得混乱(参见之前的回复)。所以,这就是我的建议:
static final Semaphore SEMAPHORE = new Semaphore(1);
...
SEMAPHORE.take(); // blocks, only one thread is allowed to proceed
try{
//critical section
} finally {
SEMAPHORE.release(); // never blocks, always within a finally block
}
然后你可以使用方法getQueueLength()来知道有多少线程正在等待并替换你正在使用的AtomicInteger。见http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/Semaphore.html
synchronized(instanceMutex) {
while(< condition does not hold >) {
obj.wait();
}
// Perform required actions
}