9

私は次の簡単なコードを持っています:

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秒間待機してから、メインプログラムの終了を通知します。

しかし、10秒間気づいたのですが、私のコアの1つが100%使用されています。

この行にコメントすると:

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

CPUコアも100%で使用され、メインプログラムもRunnableの前に終了します。

この行にコメントすると:

executor.shutdown();

CPUは適切に使用されていますが、プログラムは終了しません。

前の行の両方にコメントすると、CPUは適切に使用されますが、メインプログラムは終了しません。

  1. 私のコードに何か問題がありますか?
  2. executor.shutdown(); 新しいタスクの送信を無効にするだけでなく、ある種のビジーウェイトを実行していますか?
  3. または、JVMのせいにする必要がありますか?

追加の詳細:

$ 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:お願いします、私にCountDownLatchnorを使うように頼まないでくださいnewSingleThreadScheduledExecutor。それは私が尋ねている質問とは関係ありません。ありがとう。

編集:

これが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)
4

2 に答える 2

4

実際、待っているのは忙しいです。ThreadPoolExecutor がすべてのタスクが完了するまで待機するバックオフ ロジックはないようです (これは、スレッドを正しく中断する場合 にのみ発生することに注意してください)。shutdown()

タスクを実行する準備ができているかどうかを継続的にチェックしており、そうでない場合は、タスクがスケジュールされるまでの経過時間が経過するまで再試行します。

スケジュールされたスレッド プールのシャットダウンにはトレードオフがあります (このトレードオフは実装によって課せられます)。タスクをスケジュールする準備が整うまで、またはキューに入れられたタスクが実行されなくなるまで、ビジー スピンにshutdownNowなります。ただし、返された Runnable のリストを取得して、自分で実行することができます。

于 2012-05-20T01:00:16.073 に答える
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バグデータベースをざっと検索しましたが、関連するものは何も表示されませんでした。

ネット (Google) で見つけることができるさまざまなバージョンのソース コードを見ると、(初期の) Java 1.6 と (現在の) Java 1.7 の間で getTask メソッドとその関係者が多くの作業を行ったことは明らかです。 .

JVM を最新の Java 1.6 または Java 1.7 にアップグレードすることをお勧めします。または、少なくともテスト プログラムでこれを試してください。(または、それと一緒に暮らすだけです。これはほとんどショーストッパーではありません...)


参考までに、このページには、Ubuntu にさまざまなバージョンの Java をインストールする方法についての説明が含まれています。

  • 1 つのオプション (Java 7 の場合) は、Oracle サイトからインストーラーをプルするスクリプトである「duinsoft」インストーラーを使用することです。彼らは、インストーラーをホストする deb リポジトリーもセットアップしました。

  • もう 1 つのオプションは、11.10 リポジトリにある openjdk-7-jdk または openjdk-7-jre パッケージをインストールすることです。

そして、あなたがその地域にいる間は、Java 7 用の debian パッケージ/インストーラーを提供するために、この RFE に投票することを忘れないでください.

記録として、この混乱は主に Oracleが OEM 再配布のライセンスを撤回した結果であり、「sun-java-6」パッケージを撤回しなければならなかったことを意味します。そしてもちろん、Oracle が DEB を提供していないという「小さな問題」もあります。

于 2012-05-20T01:26:28.467 に答える