为什么ScheduledExecutorService.shutdown()使用100%的CPU?

时间:2012-05-19 23:50:41

标签: java linux concurrency executor busy-waiting

我有以下简单的代码:

package main;

import java.util.concurrent.*;

public class Main {

    public static void main(String[] args) throws InterruptedException {
        new Main();
    }

    public Main() throws InterruptedException {
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
        executor.schedule(new MyRunnable(), 10, TimeUnit.SECONDS);
        System.out.println("Shutting down...");
        executor.shutdown();
        System.out.println("Awaiting termination...");
        executor.awaitTermination(Long.MAX_VALUE, TimeUnit.MINUTES);
        System.out.println("Main finished!");
    }

    private class MyRunnable implements Runnable {
        public void run() {
            System.out.println("Finished running!");
        }
    }

}

实际上,虽然我的真实代码比这复杂得多,但我可以在这些方面找出问题。代码基本上等待10秒运行runnable,然后通知主程序的结束。

然而,我注意到了10秒钟,我的核心之一是100%使用。

如果我评论这一行:

executor.awaitTermination(Long.MAX_VALUE, TimeUnit.MINUTES);

cpu核心也以100%使用,主程序也在Runnable之前完成。

如果我评论这一行:

executor.shutdown();

cpu已正确使用,但程序无法完成。

如果我评论前两行,则cpu已正确使用但主程序无法完成。

  1. 我的代码有问题吗?
  2. executor.shutdown(); 做某种忙碌的等待,而不是只是禁用提交 新任务?
  3. 或者我应该责怪JVM?
  4. 其他详细信息:

    $ java -version
    java version "1.6.0_26"
    Java(TM) SE Runtime Environment (build 1.6.0_26-b03)
    Java HotSpot(TM) Server VM (build 20.1-b02, mixed mode)
    
    $ uname -a
    Linux XPSG 2.6.32-5-686-bigmem #1 SMP Sun May 6 04:39:05 UTC 2012 i686 GNU/Linux
    

    PS:请不要让我使用CountDownLatchnewSingleThreadScheduledExecutor。这跟我问的问题无关。感谢。

    修改

    这是java转储:

    Full thread dump Java HotSpot(TM) Server VM (20.1-b02 mixed mode):
    
    "pool-1-thread-1" prio=10 tid=0x08780c00 nid=0x32ee runnable [0x6fdcc000]
       java.lang.Thread.State: RUNNABLE
        at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:943)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:907)
        at java.lang.Thread.run(Thread.java:662)
    
    "Low Memory Detector" daemon prio=10 tid=0x0874dc00 nid=0x32ec runnable [0x00000000]
       java.lang.Thread.State: RUNNABLE
    
    "C2 CompilerThread1" daemon prio=10 tid=0x0874c000 nid=0x32eb waiting on condition [0x00000000]
       java.lang.Thread.State: RUNNABLE
    
    "C2 CompilerThread0" daemon prio=10 tid=0x0874a000 nid=0x32ea waiting on condition [0x00000000]
       java.lang.Thread.State: RUNNABLE
    
    "Signal Dispatcher" daemon prio=10 tid=0x08748800 nid=0x32e9 waiting on condition [0x00000000]
       java.lang.Thread.State: RUNNABLE
    
    "Finalizer" daemon prio=10 tid=0x0873a000 nid=0x32e8 in Object.wait() [0x70360000]
       java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x9e8f1150> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:118)
        - locked <0x9e8f1150> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:134)
        at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:159)
    
    "Reference Handler" daemon prio=10 tid=0x08735400 nid=0x32e7 in Object.wait() [0x703b1000]
       java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x9e8f1050> (a java.lang.ref.Reference$Lock)
        at java.lang.Object.wait(Object.java:485)
        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:116)
        - locked <0x9e8f1050> (a java.lang.ref.Reference$Lock)
    
    "main" prio=10 tid=0x086b5c00 nid=0x32e3 waiting on condition [0xb6927000]
       java.lang.Thread.State: TIMED_WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x9e958998> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:198)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2025)
        at java.util.concurrent.ThreadPoolExecutor.awaitTermination(ThreadPoolExecutor.java:1253)
        at main.Main.<init>(Main.java:19)
        at main.Main.main(Main.java:10)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader.main(JarRsrcLoader.java:58)
    
    "VM Thread" prio=10 tid=0x08731800 nid=0x32e6 runnable 
    
    "GC task thread#0 (ParallelGC)" prio=10 tid=0x086bd000 nid=0x32e4 runnable 
    
    "GC task thread#1 (ParallelGC)" prio=10 tid=0x086be400 nid=0x32e5 runnable 
    
    "VM Periodic Task Thread" prio=10 tid=0x0874fc00 nid=0x32ed waiting on condition 
    
    JNI global references: 931
    
    Heap
     PSYoungGen      total 18752K, used 645K [0x9e8f0000, 0x9fdd0000, 0xb3790000)
      eden space 16128K, 4% used [0x9e8f0000,0x9e991510,0x9f8b0000)
      from space 2624K, 0% used [0x9fb40000,0x9fb40000,0x9fdd0000)
      to   space 2624K, 0% used [0x9f8b0000,0x9f8b0000,0x9fb40000)
     PSOldGen        total 42880K, used 0K [0x74b90000, 0x77570000, 0x9e8f0000)
      object space 42880K, 0% used [0x74b90000,0x74b90000,0x77570000)
     PSPermGen       total 16384K, used 2216K [0x70b90000, 0x71b90000, 0x74b90000)
      object space 16384K, 13% used [0x70b90000,0x70dba198,0x71b90000)
    

2 个答案:

答案 0 :(得分:4)

实际上正忙着等待。 ThreadPoolExecutor似乎没有退避逻辑等待所有任务完成(注意,这只发生在shutdown(),否则它会正确挂起线程)。

如果任务没有再次尝试,那么它会不断检查以确定任务是否已准备好执行,直到任务计划完成所经过的时间为止。

关闭计划的线程池有一个权衡(实现强制执行此权衡)。在任务准备好安排或shutdownNow没有执行任何队列任务之前,它将忙于旋转。但是,您可以获取返回的Runnable列表并自行执行它们。

答案 1 :(得分:3)

这是一个特定于平台的问题。当我在我的机器上运行测试程序时,在10秒关闭期间CPU使用率几乎为零......根据我机器的CPU使用情况监控。

$ java -version
java version "1.7.0_03"
Java(TM) SE Runtime Environment (build 1.7.0_03-b04)
Java HotSpot(TM) Client VM (build 22.1-b02, mixed mode, sharing)

我粗略搜索了Java漏洞数据库,没有任何相关信息显示出来。

看看你可以在网上找到的不同版本的源代码(谷歌),很明显getTask方法和朋友们已经在(早期)Java 1.6和(当前)之间完成了很多工作。 )Java 1.7。

我建议您尝试将JVM升级到最新的Java 1.6或Java 1.7。或者至少试试这个测试程序。 (或者只是和它一起生活。这几乎不是表演的终结者......)


仅供参考,this page包含有关如何在Ubuntu上安装各种Java版本的说明。

  • 一个选项(对于Java 7)是使用“duinsoft”安装程序,该安装程序是从Oracle站点提取安装程序的脚本。他们甚至建立了一个deb存储库来托管安装程序。

  • 另一种选择是安装11.10回购中的openjdk-7-jdk或openjdk-7-jre软件包。

当你在该地区时,不要忘记对此RFE投票provide the debian package/installer for Java 7.

为了记录,这个混乱主要是Oracle withdrawing licensing for OEM redistibution的结果,这意味着必须取消“sun-java-6”包。当然,Oracle没有提供DEB的“小问题”。