【问题标题】:Another thread going inside the mutex另一个线程进入互斥锁
【发布时间】:2011-12-02 17:35:20
【问题描述】:

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

【问题讨论】:

    标签: java multithreading thread-safety mutex synchronized


    【解决方案1】:

    我认为代码到处都是味道 :-) 实际上,创建新实例之类的事情只是访问静态字段,这会让事情变得混乱(请参阅之前的回复)。所以,这就是我的建议:

    1. 如果您正在编写新代码,则应避免使用等待/通知。查看并发包和 Effective Java 书中的 Item69。
    2. 根据我从您的代码中了解到的情况,您需要这样的信号量:
    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. 考虑使用执行器。然后,一旦你提交了任务,你就可以等待 Future。
    2. 如果您仍想使用等待/通知,请确保将所有代码放在同步块中并使用标准习语(Effective Java 中的 Item69):
    synchronized(instanceMutex) {
        while(< condition does not hold >) {
            obj.wait();
        }
    
        // Perform required actions
    }
    

    【讨论】:

      【解决方案2】:

      try 块不在同步块内。操作系统可以在任何时候暂停一个线程并恢复另一个线程,如果一个线程刚刚离开同步块并暂停另一个线程可以在第一个线程完成同步块之前或同时在逻辑上执行 try 块。

      如果您一次只需要一个线程来执行 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();
        }
      }
      

      【讨论】:

      • 他尝试的同步实际上是由Instance no 处理的——如果它大于零,则另一个线程处于临界区,另一个线程应该等待。问题是,他正在每个线程中创建他的锁定变量的新实例(您的示例中的第二行)。起初,我也失去了这种微妙之处。
      • @Hannele 问题正如我所说的那样。 Instance 方法委托给一个静态字段。拥有多个 Instance 实例不是问题。
      【解决方案3】:

      问题是您每次调用doSomething() 时都会创建no 的新实例。

      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();
      }
      

      道德:比赛条件很棘手!

      【讨论】:

      • 一般一个问号就足够了,但我认为你是对的。我会仔细看看。
      • 好了,我想我已经找到了真正的问题——除此之外,我认为您已经正确地实施了排除。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-21
      • 2011-10-12
      • 1970-01-01
      • 2023-03-24
      • 2013-01-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多