如何创建一个在应用程序运行时始终运行的线程
-
05-07-2019 - |
题
编辑:我现在确定问题与 while (true)
循环保存所有其他命令,正如我已注释掉的那样,并且应用程序部署时没有附加的异常。我不确定它有多重要,但我的 ServletContextListener
实现看起来像这样:
public class BidPushService implements ServletContextListener{
public void contextInitialized(ServletContextEvent sce) {
//Some init code not relevant, omitted for clarity
BidPushThread t= new BidPushThread();
t.setServletContext(sce.getServletContext());
t.run();
}
所以现在线程在部署应用程序时运行,但因为 while
循环被注释它没有实际意义。
当我的应用程序加载时,我需要有一个线程在后台运行,并不断(没有超时)检查某个队列中的对象。当然,一旦有对象,它就会“处理它们”,然后继续检查队列。
目前,我正在实施 ServletContextListener
界面,当应用程序加载时我被调用。在其中,我做了一些维护工作并启动了一个我继承的线程 java.lang.Thread
.
这就是我的问题开始的地方(或者我认为是这样)。在我的 run()
方法,我有一个
while (true) {
//some code which doesn't put the thread to sleep ever
}
当我尝试将我的应用程序部署到服务器时,我得到一个 java.util.concurrent.TimeOutException
。我究竟做错了什么?
我不能有一个始终运行的线程吗?当应用程序被删除时,该线程将被我的相应事件停止 ServletContextListener
.
我确实需要一些可以不间断地检查队列的东西。
非常感谢您的帮助!
编辑:这是堆栈跟踪
GlassFish: deploy is failing=
java.util.concurrent.TimeoutException
at java.util.concurrent.FutureTask$Sync.innerGet(Unknown Source)
at java.util.concurrent.FutureTask.get(Unknown Source)
at com.sun.enterprise.jst.server.sunappsrv.SunAppServerBehaviour.publishDeployedDirectory(SunAppServerBehaviour.java:710)
at com.sun.enterprise.jst.server.sunappsrv.SunAppServerBehaviour.publishModuleForGlassFishV3(SunAppServerBehaviour.java:569)
at com.sun.enterprise.jst.server.sunappsrv.SunAppServerBehaviour.publishModule(SunAppServerBehaviour.java:266)
at org.eclipse.wst.server.core.model.ServerBehaviourDelegate.publishModule(ServerBehaviourDelegate.java:948)
at org.eclipse.wst.server.core.model.ServerBehaviourDelegate.publishModules(ServerBehaviourDelegate.java:1038)
at org.eclipse.wst.server.core.model.ServerBehaviourDelegate.publish(ServerBehaviourDelegate.java:872)
at org.eclipse.wst.server.core.model.ServerBehaviourDelegate.publish(ServerBehaviourDelegate.java:708)
at org.eclipse.wst.server.core.internal.Server.publishImpl(Server.java:2690)
at org.eclipse.wst.server.core.internal.Server$PublishJob.run(Server.java:272)
at org.eclipse.core.internal.jobs.Worker.run(Worker.java:55)
我的代码:
public class BidPushThread extends Thread {
private ServletContext sc=null;
@Override
public void run() {
if (sc!=null){
final Map<String, List<AsyncContext>> aucWatchers = (Map<String, List<AsyncContext>>) sc.getAttribute("aucWatchers");
BlockingQueue<Bid> aucBids = (BlockingQueue<Bid>) sc.getAttribute("aucBids");
Executor bidExecutor = Executors.newCachedThreadPool();
final Executor watcherExecutor = Executors.newCachedThreadPool();
while(true)
{
try // There are unpublished new bid events.
{
final Bid bid = aucBids.take();
bidExecutor.execute(new Runnable(){
public void run() {
List<AsyncContext> watchers = aucWatchers.get(bid.getAuctionId());
for(final AsyncContext aCtx : watchers)
{
watcherExecutor.execute(new Runnable(){
public void run() {
// publish a new bid event to a watcher
try {
aCtx.getResponse().getWriter().print("A new bid on the item was placed. The current price "+bid.getBid()+" , next bid price is "+(bid.getBid()+1));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
};
});
}
}
});
} catch(InterruptedException e){}
}
}
}
public void setServletContext(ServletContext sc){
this.sc=sc;
}
}
对不起,格式化的混乱,但对于我的“ 4个空间缩进代码”的寿命,对我来说不起作用:阅读“BlockingQueue”并实现它,但我仍然得到完全相同的异常和堆栈跟踪。更改了上面的代码以反映“BlockingQueue”的使用
解决方案
您的代码没有启动新线程,它在同一个线程中运行循环,这就是您在部署时遇到超时错误的原因。
要启动一个线程,你必须调用start方法,而不是run方法。
public void contextInitialized(ServletContextEvent sce) {
//Some init code not relevant, omitted for clarity
BidPushThread t= new BidPushThread();
t.setServletContext(sce.getServletContext());
t.start();// run();
}
其他提示
这将是一个非常糟糕的主意。没有充分理由,你会导致100%的CPU负载。
正确的解决方案可能是在队列为空时阻塞线程。这可以通过 BlockingQueue
来实现。
Can't I have a thread which is always running? When the app is removed,
that thread is stopped by the corresponding event in my ServletContextListener.
“该线程已停止”?怎么样?您的while(true){...}循环中没有终止条件。你是怎么阻止它的?你在使用Thread.stop()方法吗?这是不安全的,并且在Java 1.1中被弃用了
如果您使用setDaemon(true),则在使用app-server的管理工具停止Web应用程序后,该线程将保持活动状态。然后,如果您重新启动Web应用程序,您将获得另一个线程。即使您尝试取消部署Web应用程序,该线程仍将继续运行,并将阻止整个Web应用程序被垃圾收集。然后重新部署下一个版本将为您提供内存中所有内容的附加副本。
如果为循环提供退出条件(例如InterruptedException或volatile“stopNow”boolean),则可以避免此问题。
看一下java.langThread.setDaemon()方法,可能就是你需要的东西