Play Framework外部线程运行机制及2.2.6版本线程变慢问题咨询
问题分析与解决方案:Play Framework 2.2.6启动线程后逐渐变慢
嘿,这个问题我一眼就看出症结了——你的线程创建逻辑有严重问题,导致系统资源被慢慢耗尽,才会出现打印间隔越来越长的情况。先看看你的代码:
@Override public void run(){ try{ System.out.println("print my server stats..."); Thread.sleep(1000); Thread t = new Thread(this); t.start(); }catch(Exception e){ } }
问题根源
这段代码的致命问题在于:每次run方法执行完sleep后,都会新建一个线程来再次执行自己。相当于每过1秒,系统里就会多一个线程。几个小时下来,系统里会堆积成千上万的线程——每个线程都要占用内存,操作系统还要不停在这些线程之间做上下文切换,CPU的大量时间都耗在了调度上,真正用来执行打印任务的时间自然越来越少,最终导致打印间隔越来越长,甚至可能引发内存溢出或者系统卡顿。
修复方案
这里给你两个靠谱的解决办法:
方案1:改成单线程循环执行
不需要每次创建新线程,直接在当前线程里循环运行,这样始终只有一个线程在工作:
@Override public void run(){ // 增加中断判断,方便后续优雅停止线程 while (!Thread.currentThread().isInterrupted()) { try{ System.out.println("print my server stats..."); Thread.sleep(1000); }catch(InterruptedException e){ // 保留线程的中断状态,确保后续逻辑能感知到中断 Thread.currentThread().interrupt(); break; }catch(Exception e){ // 捕获其他异常,避免意外打断循环 e.printStackTrace(); } } }
然后在onStart方法里只需要启动一次这个线程,记得设置为守护线程,这样应用关闭时它会自动退出:
// 在你的Application类的onStart方法中 Thread statsThread = new Thread(你的Runnable实例); statsThread.setDaemon(true); statsThread.start();
方案2:使用ScheduledExecutorService(更推荐)
在Play Framework里,用Java自带的ScheduledExecutorService来做定时任务会更可靠,它能帮你自动管理线程,避免手动创建线程带来的泄漏问题:
// 在onStart方法中初始化定时任务 ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); // 立即执行,之后每1秒执行一次打印逻辑 scheduler.scheduleAtFixedRate(() -> { System.out.println("print my server stats..."); }, 0, 1, TimeUnit.SECONDS); // 记得在onStop方法中关闭调度器,避免资源泄漏 @Override public void onStop() { scheduler.shutdown(); try { // 等待1秒让任务完成,超时则强制关闭 if (!scheduler.awaitTermination(1, TimeUnit.SECONDS)) { scheduler.shutdownNow(); } } catch (InterruptedException e) { scheduler.shutdownNow(); } }
补充提醒
不管用哪种方案,都要注意线程的优雅停止——尤其是在Play应用关闭时,要确保线程能正常退出,避免资源泄漏。另外,不要忽略异常处理,不然一旦出现未捕获的异常,你的定时任务就会意外停止。
内容的提问来源于stack exchange,提问作者nafas
相关产品推荐
相关产品推荐

