gpt4 book ai didi

java - 在 stdin 上阻塞使 Java 进程需要多 350 毫秒才能退出

转载 作者:IT老高 更新时间:2023-10-28 20:34:47 25 4
gpt4 key购买 nike

我有一个Java程序如下:

public class foo{

public static void main(String[] args) throws Exception{
Thread t = new Thread(
new Runnable() {
public void run() {
try{System.in.read();}catch(Exception e){}
}
}
);
t.setDaemon(true);
t.start();
Thread.sleep(10); // Make sure it hits the read() call
t.interrupt();
t.stop();
System.exit(0);
}
}

使用 time java foo 运行此 ( System.in.read )使用 System.in.read 运行时,当前调用需要大约 480 毫秒才能退出。注释掉的调用大约需要 120 毫秒才能退出

我原以为一旦主线程结束,程序就会终止,但很明显,还有另外 300 毫秒的延迟(您可以通过在 Thread.sleep 之后添加 println 来看到这一点)。我试过 t.interrupt t.stop System.exit这应该“立即”停止事情,但他们似乎都不能让程序跳过它的 350ms 额外退出延迟似乎什么都不做。

任何人都知道为什么会这样,如果我能做些什么来避免这种延迟?

最佳答案

事实证明,除了收集坏消息外,还有一些解决方法,请参阅这篇文章的最底部。

这不是一个完整的答复,只是我脑海中出现了一个带有套接字的支票。
基于此和System.in.read()我也复制了一个实验,延迟可能是向操作系统发出未完成的同步 I/O 请求的代价。 (编辑:实际上这是一个显式等待,当 VM 关闭时线程未正常退出时启动,请参见水平线下方)

(我用 while(true); 结束线程,所以它(他们)永远不会过早退出)

  • 如果您创建绑定(bind)套接字 final ServerSocket srv=new ServerSocket(0); , 退出仍然是“正常”
  • 如果您 srv.accept(); ,你突然有额外的等待
  • 如果您使用 Socket s=new Socket("localhost",srv.getLocalPort()); 创建一个“内部”守护进程线程, 和 Socket s=srv.accept();在外面,它再次变得“正常”
  • 但是,如果您调用 s.getInputStream().read();对于其中任何一个,您都需要再次等待
  • 如果您同时使用两个套接字,则额外的等待时间会延长一点(远小于 300,但对我来说持续 20-50 毫秒)
  • 有内线程,也有可能卡在new Socket(...);行,如果 accept()不在外部调用。这也有额外的等待

  • 因此,拥有套接字(只是绑定(bind)或什至连接)不是问题,但是等待某些事情发生( accept()read() )会引入一些东西。

    代码(此变体命中两个 s.getInputSteam().read() -s)
    import java.net.*;
    public class foo{
    public static void main(String[] args) throws Exception{
    Thread t = new Thread(
    new Runnable() {
    public void run() {
    try{
    final ServerSocket srv=new ServerSocket(0);

    Thread t=new Thread(new Runnable(){
    public void run(){
    try{
    Socket s=new Socket("localhost",srv.getLocalPort());
    s.getInputStream().read();

    while(true);
    }catch(Exception ex){}
    }});
    t.setDaemon(true);
    t.start();

    Socket s=srv.accept();

    s.getInputStream().read();

    while(true);
    }catch(Exception ex){}
    }
    }
    );
    t.setDaemon(true);
    t.start();
    Thread.sleep(1000);
    }
    }

    我还尝试了评论中出现的内容:访问(我刚刚使用 static )到 ServerSocket srv , int port , Socket s1,s2 ,在Java端杀东西更快: close()srv/ s1/ s2关闭 accept()read()调用非常快,并且用于关闭 accept()特别是, new Socket("localhost",port)也可以工作(只是在实际连接同时到达时它具有竞争条件)。也可以使用 close() 关闭连接尝试,为此只需要一个对象(因此必须使用 s1=new Socket();s1.connect(new InetSocketAddress("localhost",srv.getLocalPort())); 而不是连接构造函数)。

    TL;DR:这对你来说重要吗?完全没有:我试过 System.in.close();并且它对 System.in.read();绝对没有影响.

    新的坏消息。当线程处于 native 代码中,并且该 native 代码不检查“安全点”时,这是关闭过程的最后一步 waits for 300 milliseconds , 最低限度:

    // [...] In theory, we
    // don't have to wait for user threads to be quiescent, but it's always
    // better to terminate VM when current thread is the only active thread, so
    // wait for user threads too. Numbers are in 10 milliseconds.
    int max_wait_user_thread = 30; // at least 300 milliseconds


    并且等待是徒劳的,因为线程正在执行一个简单的 fread/proc/self/fd/0
    虽然 read (还有 recv)被包裹在一些神奇的 RESTARTABLE 中循环的东西( https://github.com/openjdk-mirror/jdk7u-hotspot/blob/master/src/os/linux/vm/os_linux.inline.hpp#L168read 有点低——它是另一个文件中 fread 的包装器),它似乎知道 EINTR
    #define RESTARTABLE(_cmd, _result) do { \
    _result = _cmd; \
    } while(((int)_result == OS_ERR) && (errno == EINTR))

    [...]

    inline size_t os::restartable_read(int fd, void *buf, unsigned int nBytes) {
    size_t res;
    RESTARTABLE( (size_t) ::read(fd, buf, (size_t) nBytes), res);
    return res;
    }


    ,但这在任何地方都没有发生,而且这里和那里有一些评论,他们不想干扰 libpthread 自己的信号和处理程序。根据 SO 上的一些问题(如 How to interrupt a fread call? ),它可能无论如何都行不通。

    图书馆方面, readSingle ( https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/native/java/io/io_util.c#L38 ) 是被调用的方法:

    jint
    readSingle(JNIEnv *env, jobject this, jfieldID fid) {
    jint nread;
    char ret;
    FD fd = GET_FD(this, fid);
    if (fd == -1) {
    JNU_ThrowIOException(env, "Stream Closed");
    return -1;
    }
    nread = (jint)IO_Read(fd, &ret, 1);
    if (nread == 0) { /* EOF */
    return -1;
    } else if (nread == JVM_IO_ERR) { /* error */
    JNU_ThrowIOExceptionWithLastError(env, "Read error");
    } else if (nread == JVM_IO_INTR) {
    JNU_ThrowByName(env, "java/io/InterruptedIOException", NULL);
    }
    return ret & 0xFF;
    }


    它能够在 JRE 级别处理“被中断”,但该部分不会作为 fread 执行不返回(在所有非 Windows 的情况下, IO_Read #define -d 到 JVM_Read ,这只是前面提到的 wrapper restartable_read )

    所以,这是设计使然。

    有效的一件事是提供您自己的 System.in (尽管是 final 有一个 setIn() 用于此目的的方法,在 JNI 中进行非标准交换)。但是涉及到轮询,所以有点难看:
    import java.io.*;
    public class foo{
    public static void main(String[] args) throws Exception{

    System.setIn(new InputStream() {
    InputStream in=System.in;
    @Override
    public int read() throws IOException {
    while(in.available()==0)try{Thread.sleep(100);}catch(Exception ex){}
    return in.read();
    }
    });

    Thread t = new Thread(
    new Runnable() {
    public void run() {
    try{
    System.out.println(System.in.read());
    while(true);
    }catch(Exception ex){}
    }
    }
    );
    t.setDaemon(true);
    t.start();
    Thread.sleep(1000);
    }
    }

    Thread.sleep()InputStream.read()您可以在无响应或使用 CPU cooking 之间取得平衡。 Thread.sleep()正确检查是否被关闭,因此即使您将其设置为 10000,该过程也会快速退出。

    关于java - 在 stdin 上阻塞使 Java 进程需要多 350 毫秒才能退出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48951611/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com