我有一个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
}需要大约480毫秒才能退出,同时运行System.in.read
调用注释需要大约120毫秒退出
我曾经想过,一旦主线程到达终点,程序就会终止,但很明显,还有另外300ms的滞后(你可以通过在Thread.sleep
之后添加一个println来看到这一点)。我尝试了t.interrupt
t.stop
System.exit
这应该立即停止事情"但是他们似乎都没有能够让程序跳过350毫秒的额外退出延迟看似无所事事。
任何人都知道为什么会这样,如果我有什么办法可以避免这种延迟?
答案 0 :(得分:18)
除了收集坏消息之外,还有一些解决方法,请参阅本文的最后部分。
这不是一个完整的回复,只是一个支票弹出我的脑海,插座。
基于此和我已经复制的System.in.read()
实验,延迟可能是对操作系统提供出色的同步I / O请求的成本。 (编辑:实际上它是一个明确的等待,当线程在VM关闭时没有正常退出时启动,见水平线下方)
(我用while(true);
结束线程,所以它(它们)永远不会过早退出)
final ServerSocket srv=new ServerSocket(0);
,则退出仍然是正常的' srv.accept();
,你突然有额外的等待Socket s=new Socket("localhost",srv.getLocalPort());
和Socket s=srv.accept();
外部,它变为“正常”'再次s.getInputStream().read();
,那么您还有额外的等待new Socket(...);
,accept()
行上。这也有额外的等待所以有套接字(只是绑定甚至连接)不是问题,但等待某事发生(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()
1}}特别是,new Socket("localhost",port)
也可以工作(当实际连接同时到达时,它就具有竞争条件)。使用close()
也可以关闭连接尝试,只需要一个对象(因此必须使用s1=new Socket();s1.connect(new InetSocketAddress("localhost",srv.getLocalPort()));
而不是连接构造函数。)
TL; DR:对你来说重要吗?完全没有:我尝试了System.in.close();
,它对System.in.read();
完全没有影响。
新的坏消息。当一个线程使用本机代码,并且该本机代码不检查' safepoint'时,关闭过程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#L168和read
稍微低一点 - 但它是一个另一个文件中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
,因此该部分无法执行,这只是前面提到的restartable_read
的{{3}}
所以,它是设计的。
有一件事是提供你自己的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烹饪之间取得平衡。 Thread.sleep()
正确检查是否已关闭,因此即使您将其设置为10000,该过程也会快速退出。
答案 1 :(得分:8)
正如其他人所评论的那样,线程中断不会导致阻塞I / O调用立即停止。等待正在发生,因为系统正在等待来自File(stdin)的输入。如果您要为程序提供一些初始输入,您会发现它完成得更快:
$ time java -cp build/libs/testjava.jar Foo
real 0m0.395s
user 0m0.040s
sys 0m0.008s
$ time java -cp build/libs/testjava.jar Foo <<< test
real 0m0.064s
user 0m0.036s
sys 0m0.012s
如果您想避免等待I / O线程,那么您可以先检查可用性。然后,您可以使用可中断的方法执行等待,例如Thread.sleep
:
while (true) {
try {
if (System.in.available() > 0) {
System.in.read();
}
Thread.sleep(400);
}
catch(Exception e) {
}
}
此程序每次都会快速退出:
$ time java -cp build/libs/testjava.jar Foo
real 0m0.065s
user 0m0.040s
sys 0m0.008s
$ time java -cp build/libs/testjava.jar Foo <<< test
real 0m0.065s
user 0m0.040s
sys 0m0.008s