【问题标题】:Multi-threaded code in a static initalizer block produces deadlock静态初始化程序块中的多线程代码产生死锁
【发布时间】:2012-10-23 15:20:33
【问题描述】:

我有一个班级需要提供快速分类服务。例如,我想编写像“classify("Ac Kd Kh 3c 3s")”这样快速返回 TWO_PAIR 的代码。 (这不是应用程序,但你得到了 jist)

因为我需要快速分类,所以我想预先计算并存储一个查找表,其中列出了所有可能输入的分类输出。为了节省时间,我想并行化这个预计算。但是,尝试从第二个线程使用“classifySlowly”会产生死锁。

public class Classifcation Service {
  enum CLASS {TYPE_A, TYPE_B, ...};

  static CLASS[] preComputedClassLookUpTable;

  static {
    preComputedClassLookUpTable = constructLookUpTableInParallel();
  }

  //Note: using this method from with constructLookUpTableInParallel causes deadlock
  private static CLASS classifySlowly(Object classifyMe) {
    //do time intensive work to classify the input
    // -- uses no other methods from this class
    return classification;
  }

  public static CLASS classify(Object classifyMe) {
    //use the lookup table to do a quick classification
    return classification;
  }   
}

所以我的问题是:有没有办法在静态初始化块中预先计算这个查找表 IN PARALLEL?

我看到的唯一(差)选择是从以下位置切换:

preComputedClassLookUpTable = constructLookUpTableInParallel();

收件人:

preComputeClassLookUpTable = loadLookUpTableFromFile();
if(preComputedClassLookUpTable == null) {
  System.out.println("WARNING:  Construction incomplete, Must call computeAndSaveLookUpTableFile();}
}

我认为这太过分了,但这里是constructLookUpTableInParallel的实现

private static CLASS[] constructLookUpTableInParallel() {

  //build a collection of Runnables by iterating over all possible input Objects
  //wrap each possible input in an anonymous Runnable that calls classifySlowly.

  //submit the collection of Runnables to a new ExecutorService
  //process runnables...
  //shutdown executor service      
}

////////措辞不好的原始问题结束 ///////////

有点干净的解决方案是将classifySlowly(Object分类Me)和classify(Object分类Me)方法分成两个不同的类。

这将允许包含“public static CLASSclassifySlowly(ObjectclassifyMe)”的(第一个)类在包含“public static CLASSclassifyQuickly(ObjectclassifyMe)”的(第二个)类需要使用时完全加载分类慢速方法。现在,第二个静态初始化块不需要任何自己的静态方法,它可以完全并行化。

【问题讨论】:

  • 如果不发布多线程代码,调试多线程代码非常困难。 constructLookUpTableInParallel 在做什么?
  • 你怎么知道它是死锁?为什么loadLookUpTableFromFile 是糟糕的选择?预先计算并存储在文件中以加快初始化速度可能是个好主意
  • @JohnB -- 它隐藏了这种复杂性。如果constructLookUpTableInParallel 没有启动新线程,则程序可以正常工作。但是,如果您必须知道,它会创建一组 Runnables 来执行每个分类任务,然后将这些 Runnables 提交给 ExecutorService
  • 在没有同步块或其他锁的情况下怎么死锁?
  • 如果你“隐藏”了所有相关的细节,祝你好运

标签: java multithreading static


【解决方案1】:

“所以我的问题是:有没有办法在静态初始化块中预先计算这个查找表 IN PARALLEL?”

是的,几乎可以肯定有办法。只需新建数组并为每个数组元素启动一个 Runnable。给每个Runnable对数组的引用,并索引它正在计算的内容,然后让它在不加锁的情况下进行计算,然后在将结果分配给数组元素时加锁。

注意/免责声明:此答案基于问题中提供的相当不完整的信息...

【讨论】:

  • 我尝试了一些非常相似的方法,但它不起作用,因为在静态初始化块完成之前,runnables 无法访问classifySlowly。
  • 因此,更改顺序或拆分静态初始化块,甚至将其拆分为多个类。为什么classifySlowly 需要在同一个类中?
  • 为了简单起见,我希望所有东西都在同一个类中。我将尝试使用此解决方案使用 2 个不同的静态初始化程序块……但我想我已经尝试过了。
  • 一般来说,作为个人意见,尤其是在 Java 语言中(有大量的枚举、内部类、匿名类......),过于努力地将事物保留在一个类中并不是一件好事习惯。当事情不再适合一个类时,重构!当一个包有太多文件时,将东西移动到子包中! Java IDE 也让这一切变得相当轻松。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-03
  • 2013-12-28
  • 1970-01-01
  • 2011-11-22
  • 2013-09-03
相关资源
最近更新 更多