目前我使用以下方法执行本机进程:
java.lang.Process process = Runtime.getRuntime().exec(command);
int returnCode = process.waitFor();
Run Code Online (Sandbox Code Playgroud)
假设代替等待程序返回,我希望在一定时间过去后终止.我该怎么做呢?
任何人都可以指导我如何在java中使用简单的超时?基本上在我的项目中我正在执行一个语句br.readLine()
,它正在读取调制解调器的响应.但有时调制解调器没有响应.为此我想添加一个超时.我正在寻找像这样的代码:
try {
String s= br.readLine();
} catch(TimeoutException e) {
System.out.println("Time out has occurred");
}
Run Code Online (Sandbox Code Playgroud) 我正在向Callable
a 提交对象ThreadPoolExecutor
,它们似乎在记忆中徘徊.
寻找与Eclipse的MAT工具堆转储看到Callable
的物体正在被引用FutureTask$Sync
的可调用的变量.这FutureTask$Sync
是由一个被引用FutureTask
的同步变量.这FutureTask
是由引用FutureTask$Sync
的这个$ 0变量.
我已经读过这个(这里,这里,以及SO),似乎FutureTask
可调用包含在ThreadPoolExecutor
's submit()中,它永远保存了对callable的引用.
我感到困惑的是如何确保FutureTask
收集垃圾,以便它不会继续保持内存中的可调用内容,并保存可调用内容可能保留在内存中的任何内容?
只是为了提供有关我的特定情况的更多详细信息,我试图以ThreadPoolExecutor
允许在需要时取消所有提交的任务的方式实现它.我尝试了好几种不同的方法,我发现在SO和其他地方,如完全查封了执行者(与shutdown()
,shutdownNow()
等),并保持期货的列表中返回submit()
,并呼吁取消所有的人,然后清除期货的列表.理想情况下,我不想将其关闭,只需cancel()
在需要时清除.
所有这些方法似乎没有什么区别.如果我向游泳池提交一个可调用的游戏,它很可能会最终粘在游泳池中.
我究竟做错了什么?
谢谢.
编辑:
根据要求,这是ThreadPoolExecutor的构造函数.
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
Run Code Online (Sandbox Code Playgroud)
经过进一步测试后,我可以看到,如果我让已经提交给ThreadPoolExecutor的任务完成,那么就没有泄漏.如果我试图取消它们,例如:
shutdownNow()
Run Code Online (Sandbox Code Playgroud)
或保存对未来的引用并稍后调用取消:
Future referenceToCancelLater = submit(task); …
Run Code Online (Sandbox Code Playgroud) 我将Callable任务(使用submit())提交给ExecutionService的实现.偶尔我似乎遇到了死锁,但无法在哪里或为什么会发生这种情况,所以我想在任务上设置超时,我不清楚它是怎么做到的?
我是不是该
选项1似乎是唯一可行的解决方案,但是它呢?
更多细节
我认为,如果有助于解决方案,可能需要更详细地解释该流程的工作原理.
可调用任务P1启动并在文件夹及其中的所有文件和文件夹上工作,并开始将歌曲分组,在ExecutorService ES1中运行,只有一个P1实例提交给ES1.
我们还有三个其他可调用类:P2,P3和P4 - 每个类都有自己的相关Executor服务,ES2,ES3,Es4).一旦P1创建了一个组,它就会将一个任务提交给相关的ES,并将该组作为数据传递,即它可以将一个P2实例提交给E2,P3或者提交给P3或P4提交给E4,它选择哪一个取决于详细信息.分组,P2,P3和P4都做不同的事情.
假设它已经提交了P2的实例,P2将通过将P3提交到E3或P4提交到E4来完成处理.它的单向管道P3只能提交给P4,一旦所有任务都提交给P4,P4完成了处理完成的所有任务.
我们通过构建ES1,ES2,ES3和ES4完成处理,将任务提交给P1,然后依次调用每个ExecutorService上的shutdown(),这样,在P1完成提交所有组之后,shutdown()将不会返回,然后调用shutdown()在ES2上,直到ES2清除了它的P2任务队列才会返回.
一切都停止了我假设一些过程阻止其他进程继续进行,所以此时我想要一种取消过程的方法,这种过程需要太长时间,以便其他人可以继续,这远远不如它只是无限期地悬挂
答案更新
我按照建议尝试使用invokeAny(),它有点工作.如果P1将一个P2实例提交给E2,那么它在完成之前就会等待,这是好的,因为当使用submit()时它只返回它没有进一步处理的任何方式,但有两个问题:
每个ExecutorService使用500的有界队列,其想法是如果P2比P1慢得多,我们就不会将内容堆叠到ES2上并最终耗尽内存.所以现在P1没有完成,直到他们调用的任务完成后,队列实际上更小,因为它们不仅包括等待ES2上的插槽完成的任务,而且它们包含已经提交给ES2但正在等待它的任务完.
管道是链接的,所以如果我们在从P1提交的任务上使用invokeAny,从P2和P3和P4提交任务,那么当任务从P1提交到P2时,它将不会返回,直到后续处理从E4完成!
我只是偶然发现了守护程序线程的奇怪行为,我无法解释.我已将代码缩减为最小,完整且可验证的样本:
public static void main(String[] args) throws InterruptedException {
Thread runner = new Thread(() -> {
final int SIZE = 350_000;
for (int i = 0; i < SIZE; i++) {
for (int j = i + 1; j < SIZE; j++) {
if (i*j == SIZE * SIZE - 1) {
return;
}
}
}
});
runner.setDaemon(true);
runner.start();
// Thread.sleep(1000);
System.out.println("Exiting.");
}
Run Code Online (Sandbox Code Playgroud)
runner
线程执行的代码大约需要12秒才能在我的盒子上终止,我们对它的作用不感兴趣,因为我只需要花一些时间来计算.
如果此片段按原样运行,它将按预期工作:它在启动后终止.如果我取消注释该Thread.sleep(1000)
行并运行该程序,它将工作约12秒,然后打印出"退出"并终止.
据我所知,守护程序线程是如何工作的,我希望这段代码运行1秒然后终止执行,因为运行的唯一用户线程是使用main()方法启动的那个(runner
后台守护程序线程)一旦1000毫秒通过,它就会到达执行结束,JVM应该停止.此外,看起来很奇怪"退出"仅在12秒后打印,而不是在程序启动时打印.
我错了吗?如何实现所需的行为(暂停一秒然后停止,独立于跑步者线程正在做什么)?
我在Linux机器上使用64位Oracle JDK 1.8.0_112,如果从IDE或命令行启动它也具有相同的行为.
谢谢,安德烈
我有一个线程池:
ThreadPoolExecutor pool = new ThreadPoolExecutor(cores, 50, 30L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3000));
Run Code Online (Sandbox Code Playgroud)
然后我跑:
try {
pool.execute(() ->
{
//Very very long task, fetching from an external URL
});
}catch(Exception e){
e.printStackTrace();
}
Run Code Online (Sandbox Code Playgroud)
我从来没有得到过例外,这段代码等了好几分钟.我应该怎么做才能在30秒内取消?
如何等待2分钟完成方法,但如果没有完成则退出并继续?
我想等待2分钟才能完成方法,但是如果它在2分钟内没有完成,那么退出执行并继续前进.
java ×7
threadpool ×2
callable ×1
delay ×1
executor ×1
futuretask ×1
memory-leaks ×1
process ×1
timeout ×1