当我尝试终止ExecutorService
时,GAE会永远阻止。以下小样本:
ThreadFactory threadFactory = ThreadManager.currentRequestThreadFactory();
ExecutorService pool = Executors.newSingleThreadExecutor(threadFactory);
Future<String> future = pool.submit(new Callable<String>() {
public String call() throws Exception {
return "Hello from Thread";
}
});
LOG.info("Result is: [" + future.get() + "]. Pool expected to be idle now");
pool.shutdown();
if (!pool.awaitTermination(1, TimeUnit.SECONDS)) {
LOG.info("Pool does not like shutdown()");
pool.shutdownNow();
if (!pool.awaitTermination(1, TimeUnit.SECONDS)) {
LOG.info("Pool does not even like shutdownNow()");
}
}
在本地运行时,相同的代码无阻塞地运行,在AppEngine上部署时,它只是阻塞而不会终止。超时可以增加,直到60秒的请求限制强制代码中断。
这似乎是标准JVM的一个微妙但危险的区别。经常发现清理代码可能会导致您的服务中断。 ThreadManager
documentation提到线程有点特殊,但据我所知,它们是可中断的并且意味着终止。
由于等待终止只是毫无意义,可以打电话给pool.shutdown()
,然后假设一切都没问题吗?运行线程是泄漏内存的好方法..
Thread
时,一切正常。稍微复杂的例子:
final CountDownLatch threadEnter = new CountDownLatch(1);
final Object wait4Interrupt = new Object();
Runnable task = new Runnable() {
public void run() {
synchronized (wait4Interrupt) {
threadEnter.countDown();
try {
wait4Interrupt.wait();
} catch (InterruptedException e) {
// expected to happen since nothing is going to notify()
LOG.info("Thread got interrupted.");
Thread.currentThread().interrupt();
}
}
}
};
Thread thread = ThreadManager.createThreadForCurrentRequest(task);
// not started state
LOG.info("Thread log #1: " + thread + " " + thread.getState());
thread.start();
threadEnter.await();
// thread is inside synchronized / already waiting
synchronized (wait4Interrupt) {
// => guaranteed that thread is in waiting state here
LOG.info("Thread log #2: " + thread + " " + thread.getState());
thread.interrupt();
}
thread.join(1000);
// thread is dead
LOG.info("Thread log #3: " + thread + " " + thread.getState());
生成的日志:
I 16:08:37.213 Thread log #1: Thread[Thread-7,5,Request #0] NEW
I 16:08:37.216 Thread log #2: Thread[Thread-7,5,Request #0] WAITING
I 16:08:37.216 Thread got interrupted.
I 16:08:37.217 Thread log #3: Thread[Thread-7,5,] TERMINATED
工厂返回的线程未启动,它支持wait&amp;中断就好了,它可以是join()'d并在之后终止。 ExecutorService
还想做什么?
pool.toString()
导致后,来自示例#1的 shutdown()
java.util.concurrent.ThreadPoolExecutor@175434a
[Shutting down, pool size = 1, active threads = 0, queued tasks = 0, completed tasks = 1]
这也表明它不是由未终止线程引起的问题,因为它表示active threads = 0
。
在完成任务之前被告知这样做时,池会很好地关闭。 500 ms后,以下内容正确终止。添加future.get()
会再次显示原始问题。
Future<String> future = pool.submit(new Callable<String>() {
public String call() throws Exception {
// sleep a bit so pool is "busy" when we're trying to shutdown.
Thread.sleep(500);
return "Hello from Thread";
}
});
// get here = evil
pool.shutdown();
pool.awaitTermination(2, TimeUnit.SECONDS);
=&GT;问题似乎只发生在空闲池上。繁忙的游泳池可以关闭。
答案 0 :(得分:2)
你是对的,App Engine上的Thread
是可以中断的。引自official docs:
应用程序可以对当前线程执行操作,例如
thread.interrupt()
。
由于它在本地运行良好,因此在生产环境中开发服务器和沙箱之间存在差异。
我认为开发服务器允许多线程执行,如果没有禁用,而生产环境需要在application config file (appengine-web.xml
)中明确说明它:
<threadsafe>true</threadsafe>
除非您明确声明您的应用是线程安全的,否则提供请求只能使用1个帖子,因此您的ExecutorService
无法启动新的Thread
来执行您提交的任务,因此{{1}将阻止。它会阻止直到&#34;当前&#34;线程会结束但显然只能在提供请求后才会发生,所以你在这里遇到了死锁。