簡體   English   中英

ConcurrentHashMap崩潰使用JDK 8編譯但是以JRE 7為目標的應用程序

[英]ConcurrentHashMap crashing application compiled with JDK 8 but targeting JRE 7

我今天遇到了一個非常意想不到的錯誤,雖然我能夠找到一種方法來解決整個問題,但我不確定我是否完全理解它為什么會這樣做。

我正在使用的代碼最初是用JDK 7環境編寫的,當然是針對JRE 7.在代碼中我使用的是ConcurrentHashMap ,需要迭代映射中的鍵。 為此,我使用了map.keySet() ,根據JavaDocs,它應該返回一個Set<K> 這很好用,直到我們的構建環境切換到JDK8。

當我們轉移到JDK8時,我確保在調用javac時調用1.7的目標/源。 當代碼在想要遍歷地圖的鍵時開始失敗時,我感到非常驚訝。 沒有拋出任何錯誤,沒有異常,線程只是停止了。 在做了一些研究后,我發現Java8的ConcurrentHashMap實現.keySet()方法返回一個KeySetView<K,V>

我通過使用map.keySet()切換到使用map.keys()獲取Enumeration<K>map.keys()

現在我對這個問題的猜測是,雖然項目是針對Java7編譯的,因為使用了JDK8,Java8庫被包含在內,但為什么在它出現不匹配時它沒有拋出錯誤或異常?

正如這里所要求的是代碼片段:

class MapProcessing
{
     private ConcurrentHashMap<String, Object> map = new ConcurrentHashMap<String, Object>();

     public MapProcessing()
     {
           map.put("First",new Object());
           map.put("Second",new Object());
           map.put("Third",new Object());
     } 


     public void processing()
     {
          // when calling this type of loop causes a freeze on our system.
          for(String key : map.keySet())
          {
              System.out.println(key);
          }
      }

     public void working()
     {
         // This is what I had to do to fix the problem.
         Enumeration<String> keys = map.keys();
         while(keys.hasMoreElements())
         {
              String key = keys.nextElement();
              System.out.println(key);
         }
     }
} 

我們正在使用Oracle JDK 8 build 40在Windows 2012服務器上的javac中使用1.7和1.7的目標進行編譯。

代碼使用在Windows 2012服務器上運行的Oracle JVM 7 build 25運行。

如果我使用Java 8和javac -source 1.7 -target 1.8編譯代碼,然后使用Java 7運行它,我得到一個

Exception in thread "main" java.lang.NoSuchMethodError:
  java.util.concurrent.ConcurrentHashMap.keySet()Ljava/util/concurrent/ConcurrentHashMap$KeySetView;
    at stackoverflowt.Test.processing(Test.java:20)
    at stackoverflowt.Test.main(Test.java:27)

這是因為字節代碼看起來像

public void processing();
    Code:
       0: aload_0       
       1: getfield      #4                  // Field map:Ljava/util/concurrent/ConcurrentHashMap;
       4: invokevirtual #10                 // Method java/util/concurrent/ConcurrentHashMap.keySet:()Ljava/util/concurrent/ConcurrentHashMap$KeySetView;
       7: invokevirtual #11                 // Method java/util/concurrent/ConcurrentHashMap$KeySetView.iterator:()Ljava/util/Iterator;
      10: astore_1

並明確地引用了Java 7中不存在的ConcurrentHashMap $ KeySetView。我在Mac上使用Java 1.7.0_79和1.8.0_45

如果將代碼更改為(僅使用Map接口):

private Map<String, Object> map = new ConcurrentHashMap<String, Object>();

然后它對我有用。 Bytecode然后看起來像

public void processing();
    Code:
       0: aload_0       
       1: getfield      #4                  // Field map:Ljava/util/Map;
       4: invokeinterface #10,  1           // InterfaceMethod java/util/Map.keySet:()Ljava/util/Set;
       9: invokeinterface #11,  1           // InterfaceMethod java/util/Set.iterator:()Ljava/util/Iterator;
      14: astore_1

每當使用新的JDK使用針對舊版本的-source參數構建項目時,您將收到此編譯器警告:

warning: [options] bootstrap class path not set in conjunction with -source 1.7

這篇博客文章討論了它的含義。

基本上,您會收到此警告,因為Java正在使用較舊的語言規則編譯它,但是針對較新的類庫...並且由於Oracle移動了一些內部類,因此Java 8版本存在一些兼容性問題。

修復是在編譯時使用-bootclasspath參數將其指向舊版本的rt.jar

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM