另一个线程进入互斥锁

时间:2011-12-02 17:35:20

标签: java multithreading thread-safety mutex synchronized

我不明白这段代码有什么问题。有时两个线程开始执行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 {}
}

3 个答案:

答案 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)

我认为代码闻到了所有地方:-)实际上,做一些事情,比如创建新的实例,除了访问静态字段之外什么都不会让事情变得混乱(参见之前的回复)。所以,这就是我的建议:

  1. 如果您正在编写新代码,则应避免使用wait / notify。查看并发包和Effective Java书中的Item69。
  2. 根据我对您的代码的理解,您需要一个信号量,如:
  3. 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

    1. 考虑使用遗嘱执行人。然后,一旦提交任务,就可以等待未来。
    2. 如果您仍想使用wait / notify,请确保将所有代码放在synchronized块中并使用标准习惯用法(Effective Java中的Item69):
    3. synchronized(instanceMutex) {
          while(< condition does not hold >) {
              obj.wait();
          }
      
          // Perform required actions
      }