1

ストリームを閉じて、すでにブロックしている InputStream.read() を中止しようとしていますが、read() は戻りません。

public static void main(String args[]) throws Exception
{
    ProcessBuilder builder = new ProcessBuilder("sleep", "100000");
    Process process = builder.start();
    final InputStream is = process.getInputStream();

    Thread worker = new Thread() {
        public void run() {
            try {
                int data;
                while ((data = is.read()) != -1) {
                    System.out.println("Read:" + data);
                }
            } catch(IOException e) {
                e.printStackTrace();
            }
        }
    };

    worker.start();
    Thread.sleep(2000);
    // process.destroy(); 
    is.close();
    worker.join();
}

process.destroy() を呼び出さずに、ストリームを閉じて既にブロックしている read() を中止する方法はありますか?

この質問の背景には複雑な Eclipse プラグインがあり、ほとんどの場合、ブロックしている BufferedReader.readLine() を process.destroy() を呼び出して中止しますが、readLine() が中止されないことがあります。JVM の内部 ProcessReaper スレッドとの競合が疑われます。ストリームを明示的に閉じることで、競合を回避しようとしています。

Linux と JDK 1.7.0_25-b15 を使用しています。

4

5 に答える 5

2

古い質問、新しい答え。NuProcessライブラリは、プロセスのノンブロッキング I/O を実装しているため、この状況を完全に回避できます。

免責事項: 私は NuProcess の作成者です。

于 2013-11-20T03:26:55.867 に答える
1

私は似たようなことを見てきました.私の場合、Javaスレッドがブロックされるたびに、子プロセスがファイル記述子を独自の子プロセスに与えたため、Process.destroy()呼び出された後もまだ生きていたことが判明しました。したがって、概略的には次のようになります。

java thread --> child process --> grandchild process

Process.destroy()実際にWindows版とLinux版のソースコードを確認したところ、kill 9コマンドが実行されることがわかりました。孫を殺すならkill -9命令が必要だ。JNI を使用せずにこの問題を解決する良い方法は知りませんが、Eclipse 用に作成したので、代わりにCDT Spawnerクラスを使用して運試しできますjava.lang.Runtime。ランタイム以外のいくつかの機能を提供したことは覚えていますが、この問題を解決できるかどうかは覚えていません。このクラスは、たとえば、Eclipse CDT 環境でネイティブ プロセスを起動およびデバッグするために使用されます。

バグJDK-4770092は、Windows で孫プロセスを強制終了することが難しい理由を説明しています。私が理解している限りでは、Sun/Oracle は最小公分母を提供することに決めました。そのため、Unix でも孫が殺されることはありません。

考え直して、私が間違っていて Process.destroy() が SIGTERM を送信した場合、次のシェル スクリプトはすべてのサブプロセスを Java から分離し、Linux と Windows の Cygwin の両方で機能します。

#!/bin/sh

# Initialize variables
program="$1"; shift
child_pid=0

# Register a signal handler procedure
signalHandler() {
    echo "Sending SIGKILL to process $child_pid and all its children"
    [0 = $child_pid] || kill -9 -$child_pid
    echo "Removing temporary folder: $tmp_folder"
    rm -fr "$tmp_folder"
}
trap signalHandler 0 1 2 3 15

# Create a temporary folder
tmp_folder=$(mktemp -d --quiet)

# Launch a child process in background
program "$@" >$(tmp_folder)/somefile 2>&1 &

# Remember the subprocess id
child_pid=$!

# Make tail exit when the subprocess exits
# Maybe, this is a bad idea. I haven’t tested it
# If it doesn't work, one can read from file with Java NIO
tail --follow --pid=$child_pid $(tmp_folder)/somefile
于 2013-11-25T08:04:23.837 に答える
1

あなたは試しclose()ましたが、うまくいきません。私が試みることができる他の唯一のことはThread.interrupt()、ブロックされたスレッドで呼び出すことです...しかし、それがうまくいくかどうかは疑問です。

本当の問題は、仕様 (つまり、それぞれの javadoc) が、これらが機能するかどうかを述べていないことです。それらが機能したとしても...一部のOSプラットフォーム上の一部のバージョンのJavaで...他のバージョン/プラットフォームの組み合わせで機能するという保証はありません。

于 2013-11-11T15:17:58.207 に答える
1

ストリームから読み取る前に、利用可能なバイト数をチェックすることで解決した同様の問題がありました。あなたの例を変更する:

public static void main(String args[]) throws Exception
{
    ProcessBuilder builder = new ProcessBuilder("sleep", "100000");
    Process process = builder.start();
    final InputStream is = process.getInputStream();
    final AtomicBoolean stopReading = new AtomicBoolean(false);
    final long CHECK_INTERVAL = 100; // msec.

    Thread worker = new Thread() {
        public void run() {
            try {
                workerLoop:
                while (true) {
                    int availableBytes = is.available(); // this call should be non-blocking
                    if (availableBytes > 0) {
                        byte[] buffer = new byte[availableBytes];
                        int c = is.read(buffer); // this call should be non-blocking
                        for (int i = 0; i < c; i++) {
                            if (buffer[i] == -1) // standard check for the end of stream
                            {
                                break workerLoop;
                            }
                            // do something with the data....
                        }                        
                    } else if (stopReading.get()) {
                        break;
                    } else {
                        Thread.sleep(CHECK_INTERVAL);
                    }
                }
            } catch(Exception e) {
                e.printStackTrace();
            }
        }
    };

    worker.start();
    Thread.sleep(2000);
    stopReading.set(true); // request to terminate the thread
    worker.join();
    is.close();
} 

ブール値フラグを使用する代わりに、ワーカー スレッドを中断するようにコードを変更することができます。

Windows 10 と Oracle Java 8 を使用しています。

于 2019-12-08T04:52:09.707 に答える
-1

Java 1.7 では確かなことは言えないと思いますが、Java 1.6 ではこれを実行しました。3 つのスレッドが必要です。

  • ストリームを読み取るスレッド
  • 読み取りスレッドを監視する監視スレッド
  • メインスレッド (自動的に作成されますが、忘れないようにしたいだけです。)

読み取りスレッドは、それとモニター スレッドの間で共有される参照を更新する必要がありますが、さらに重要なこととして、モニターには読み取りスレッドへの参照が必要です。 読み取りスレッドは、読み取りごとに時間値を更新します。モニターは一定時間スリープし、起動するたびにそのタイム スタンプをチェックします。前回の時刻と現在の時刻の差がある値を超えると、モニターは読み取りスレッドに割り込みを強制し、それ自体も停止します。

これはおそらく、メインとモニターの 2 つのスレッドだけで実行できます。メイン スレッドは読み取りと実行を行いmonitor.setReaderThread(Thread.currentThread()); monitorThread.start()ます。しかし、私はこれを 3 つのスレッドでしか実行していません。

于 2013-11-11T17:05:09.713 に答える