【问题标题】:Synchronized hashmap access同步 hashmap 访问
【发布时间】:2017-05-29 12:20:00
【问题描述】:

在我的电脑上,使用java 8,即使地图访问同步,以下程序也不会停止。那些同步块还不够吗?

import java.util.HashMap;
import java.util.concurrent.TimeUnit;

// Broken! - How long would you expect this program to run?
public class StopThread {
    private static HashMap<String, String> stopRequested = new HashMap<String, String>();

    public static void main(String[] args) throws InterruptedException {
        Thread backgroundThread = new Thread(new Runnable() {
            public void run() {
                int i = 0;
                synchronized (stopRequested) {
                    while (stopRequested.get("stop") == null)
                        i++;
                }
                System.out.println(i);
            }
        });
        backgroundThread.start();
        TimeUnit.SECONDS.sleep(1);
        synchronized (stopRequested) {
            stopRequested.put("stop", "true");
        }
    }
}

【问题讨论】:

    标签: java multithreading hashmap synchronization


    【解决方案1】:

    这将永远运行。同步块中的 while 循环实际上是无限的,因为它首先进入 - 因此禁止进入第二个同步块。

    【讨论】:

    • 您的答案是正确的,但请编辑它以说明如何修复问题。
    【解决方案2】:

    是的,这是意料之中的,你的 backgroundThread 在你的主线程之前持有锁,它不会释放它,直到主线程将“停止”写入地图,主线程需要锁才能将其写入“停止”所以基本上这是一个死锁。

    有几种方法可以解决这个死锁,我猜你想要做的是看看在主线程在你的地图中写入“停止”条目之前你计算了多少次。 您可以简单地在循环的每次迭代中获取和释放您的锁,这对您的场景有意义。

    while (stopRequested.get("stop") == null)
        synchronized (stopRequested) {
                    i++;
         }
    

    另一个解决方案可能是使用 concurrentHashMap,查看此link 了解更多详情

    【讨论】:

    • 您的答案是正确的,但请编辑它以说明如何修复问题。
    • @Gray 我添加了一些解决方案,尽管我认为提及问题并让人们自己尝试解决问题会更好
    • 是的,但是 SO 是一个问答网站,而不是一个问题和“尝试自己解决”网站。尽管我理解您的想法,但我倾向于使用我的答案来展示正确的模式和最佳实践。
    • @Gray 我相信解释问题也是一个答案,我认为 SO 旨在帮助开发人员克服当前问题,在这种情况下理解问题,尽管我也尊重你的观点.
    【解决方案3】:

    您有两个线程在 stopRequested 上同步。在任何给定时间只允许运行一个synchronized 块。由于几乎总是 backgroundThread 的 synchronized 块首先运行的情况,它永远不会退出,因此永远不会允许任何其他线程在 stopRequested 上同步。

    wait 和 notify 方法正是为了解决这个问题而存在的:

        try {
            int i = 0;
            synchronized (stopRequested) {
                while (stopRequested.get("stop") == null) {
                    stopRequested.wait();
                    i++;
                }
            }
            System.out.println(i);
        } catch (InterruptedException e) {
            throw new RuntimeException(i);
        }
    
    // ...
    
    synchronized (stopRequested) {
        stopRequested.put("stop", "true");
        stopRequested.notify();
    }
    

    这样做的原因是wait() 将临时自动释放同步锁,允许另一个线程在该对象上同步。

    请注意,wait() 必须在一个检查被等待条件的循环中调用,因为即使没有其他线程调用 notify(),wait() 偶尔也会返回。这种“虚假唤醒”是由于某些系统上线程的性质造成的。

    一个表现良好的线程会将整个等待循环放在一个 try/catch 块中,因此线程会在中断时退出。中断是来自其他线程的请求,要求您的线程停止正在执行的操作并干净退出。

    【讨论】:

      【解决方案4】:

      感谢您的所有回答。确实,这是一个僵局。工作同步是

      import java.util.HashMap;
      import java.util.concurrent.TimeUnit;
      
      // Broken! - How long would you expect this program to run?
      public class StopThread {
          private static HashMap<String, String> stopRequested = new HashMap<String, String>();
      
          public static void main(String[] args) throws InterruptedException {
              Thread backgroundThread = new Thread(new Runnable() {
                  public void run() {
                      int i = 0;
                      while (stopRequested())
                          i++;
                      System.out.println(i);
                  }
              });
              backgroundThread.start();
              TimeUnit.SECONDS.sleep(1);
              synchronized (stopRequested) {
                  stopRequested.put("stop", "true");
              }
          }
      
          static boolean stopRequested()
          {
              synchronized (stopRequested) {
                  return stopRequested.get("stop") == null;
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-03-06
        • 2014-01-03
        • 2011-09-13
        • 2010-11-20
        • 1970-01-01
        相关资源
        最近更新 更多