Java ConcurrentHashMap崩溃应用程序使用JDK 8编译,但目标是JRE 7

Java ConcurrentHashMap崩溃应用程序使用JDK 8编译,但目标是JRE 7,java,iteration,concurrenthashmap,Java,Iteration,Concurrenthashmap,我今天遇到了一个非常意外的错误,虽然我能够找到一种方法来解决整个问题,但我不确定我是否完全理解它为什么会这样做 我正在使用的代码最初是使用JDK 7环境编写的,当然是针对JRE 7的。在代码中,我使用了一个ConcurrentHashMap,需要迭代映射中的键。为此,我使用了map.keySet(),根据JavaDocs,它应该返回一个集。在我们的构建环境切换到JDK8之前,这一切都很好 当我们转到JDK8时,我确保在调用javac时调用的是1.7的目标/源代码。因此,当代码开始出现错误时,我非

我今天遇到了一个非常意外的错误,虽然我能够找到一种方法来解决整个问题,但我不确定我是否完全理解它为什么会这样做

我正在使用的代码最初是使用JDK 7环境编写的,当然是针对JRE 7的。在代码中,我使用了一个
ConcurrentHashMap
,需要迭代映射中的键。为此,我使用了
map.keySet()
,根据JavaDocs,它应该返回一个
集。在我们的构建环境切换到JDK8之前,这一切都很好

当我们转到JDK8时,我确保在调用javac时调用的是1.7的目标/源代码。因此,当代码开始出现错误时,我非常惊讶,因为它想要遍历映射的键。没有抛出错误,没有异常,线程只是简单地停止了。在做了一些研究之后,我发现Java8对
ConcurrentHashMap
的实现
.keySet()
方法返回一个
KeySetView

我通过从使用
map.keys()
切换到使用
map.keys()
获取
枚举来解决了这个问题

现在我想问题是,虽然项目是在使用JDK8之后针对Java7编译的,但是包含了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);
         }
     }
} 
类映射处理
{
私有ConcurrentHashMap=新ConcurrentHashMap();
公共地图处理()
{
put(“First”,newobject());
put(“第二个”,新对象());
put(“第三个”,新对象());
} 
公开作废处理()
{
//当调用这种类型的循环时,会导致系统冻结。
for(字符串键:map.keySet())
{
系统输出打印项次(键);
}
}
公共工作
{
//这就是我必须做的来解决这个问题。
枚举键=map.keys();
while(keys.hasMoreElements())
{
String key=keys.nextElement();
系统输出打印项次(键);
}
}
} 
我们正在使用Oracle JDK 8 build 40在Windows 2012服务器上使用javac中的1.7目标和1.7源代码进行编译


代码正在使用运行在Windows 2012 server上的Oracle JVM 7 build 25运行。

每当您使用针对旧版本的
-source
参数使用较新的JDK构建项目时,您将收到以下编译器警告:

警告:[选项]引导类路径未与-source 1.7一起设置

谈论它的含义

基本上,您得到此警告是因为Java使用较旧的语言规则编译它,但针对较新的类库。。。随着Oracle迁移了一些内部类,Java8版本也存在一些兼容性问题


修复方法是在编译时使用
-bootclasspath
参数将其指向旧版本中的
rt.jar

如果我使用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) 线程“main”java.lang.NoSuchMethodError中出现异常: java.util.concurrent.ConcurrentHashMap.keySet()Ljava/util/concurrent/ConcurrentHashMap$KeySetView; 在stackoverflowt.Test.processing(Test.java:20)中 在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 公共处理(); 代码: 0:aload_0 1:getfield#4//字段映射:Ljava/util/concurrent/ConcurrentHashMap; 4:invokevirtual#10//Method java/util/concurrent/ConcurrentHashMap.keySet:()Ljava/util/concurrent/ConcurrentHashMap$KeySetView; 7:invokevirtual#11//方法java/util/concurrent/ConcurrentHashMap$KeySetView.iterator:()Ljava/util/iterator; 10:astore_1 并显式引用Java7中不存在的ConcurrentHashMap$KeySetView。我在Mac上使用Java 1.7.0_79和1.8.0_45

如果将代码更改为(仅使用映射界面):

private Map=new ConcurrentHashMap();
那就对我有用了。字节码看起来像

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 公共处理(); 代码: 0:aload_0 1:getfield#4//字段映射:Ljava/util/map; 4:invokeinterface#10,1//InterfaceMethod java/util/Map.keySet:()Ljava/util/Set; 9:invokeinterface#11,1//interface方法java/util/Set.iterator:()Ljava/util/iterator; 14:astore_1
您在连接JDK 8后尝试过清理它吗?@suresh构建环境是Jenkins。当我们移动到JDK8时,我们删除了工作区,然后执行了一个新的构建。这意味着代码是刚从SVN签出然后构建的。除了ant中对“clean”目标的标准调用之外,我不知道清理它是什么意思。你能发布一个代码片段和确切的JVM版本以及供应商+操作系统吗?KeySetView实现了设置,因此这至少不应该是真正的issue@salyh按要求提供。我已经添加了我用来解决问题的解决方法。好的,因此使用
-bootclasspath
选项添加指向正确rt.jar的指针应该可以正常工作。虽然我想我的修复可能更好,因为这意味着当我们在运行JRE 8的系统上部署应用程序时,我不必担心它会崩溃。但我好奇的是,为什么在运行程序时没有出现异常或错误?对我来说,线程刚刚停止,没有引发异常。再次感谢您提供有关构建环境的信息。我会尽快解决这个问题。我们从未将
bootclasspath
选项添加到
javac
ant任务中。我会 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