我有一个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
应该“立即”停止事情,但是他们似乎都没有能够让程序跳过它350ms的额外退出延迟似乎什么都不做。
任何人都知道为什么会这样,如果有什么我可以做的,以避免这种延迟?
事实证明,除了收集坏消息之外,还有一些解决方法,请看这篇文章的最后部分。
这不是一个完整的回复,只是
一个支票突然出现在我的脑海里,带有插座。
基于此和我已经复制的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()
,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
,这只是一个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烹饪之间取得平衡。 Thread.sleep()
正确检查是否被关闭,所以即使你把它放到10000,这个过程也会快速退出。
正如其他人所评论的那样,线程中断不会导致阻塞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