我用Java创建了一个Glassfish 4.0服务器,我在ArrayList<>
上收到了一个奇怪的错误。这是我的init()
代码:
@Override
public void contextInitialized(ServletContextEvent arg0) {
init();
}
private void init() {
sessions = new ArrayList<Session>();
new Thread(new Runnable() {
@Override
public void run() {
synchronized(sessions){
while(sessions.size() == 0){
try {
Thread.sleep(1000);
System.out.println("No sessions available: " + sessions);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
本质上,当服务器启动时,会话ArrayList
被实例化,并且新的Thread开始循环,等待WebSocket连接发生。以下是WebSocket连接的代码:
@OnOpen
public void open(Session session) {
System.out.println("CONNECTED: " + session.getId());
synchronized(sessions){
sessions.add(session);
}
}
因此,带有while循环的线程应打印出来&#34;没有可用的会话:[]&#34;直到调用open(...)方法。但是,只要open方法中的代码到达synchronized(sessions) {...}
,它就会说会话为空。考虑到在创建会话init()
的{{1}}方法之前永远不会调用此方法,这没有意义。
所以,我之前做的是添加以下代码:ArrayList
然后运行同步代码。这会起作用,会话会被添加,但它不会出现在第二个帖子中。第二个线程仍然会循环并打印出没有可用的会话。我错过了什么?
堆栈跟踪:
2016-02-18T15:13:13.601-0500 |严重:java.lang.NullPointerException 在BotManager.open(BotManager.java:134)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) 在 sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 在java.lang.reflect.Method.invoke(Method.java:606)at org.glassfish.tyrus.core.AnnotatedEndpoint.callMethod(AnnotatedEndpoint.java:431) 在 org.glassfish.tyrus.core.AnnotatedEndpoint.onOpen(AnnotatedEndpoint.java:468) 在 org.glassfish.tyrus.core.EndpointWrapper.onConnect(EndpointWrapper.java:446) 在 org.glassfish.tyrus.server.TyrusEndpoint.onConnect(TyrusEndpoint.java:146) 在 org.glassfish.tyrus.websockets.DefaultWebSocket.onConnect(DefaultWebSocket.java:122) 在 org.glassfish.tyrus.servlet.TyrusHttpUpgradeHandler.init(TyrusHttpUpgradeHandler.java:98) 在 org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:777) 在 org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:673) 在com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99)at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:174) 在 org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:357) 在 org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:260) 在 com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:188) 在 org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:191) 在 org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:168) 在 org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:189) 在 org.glassfish.grizzly.filterchain.ExecutorResolver $ 9.execute(ExecutorResolver.java:119) 在 org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:288) 在 org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:206) 在 org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:136) 在 org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:114) 在 org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77) 在 org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:838) 在 org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:113) 在 org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:115) 在 org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access $ 100(WorkerThreadIOStrategy.java:55) 在 org.glassfish.grizzly.strategies.WorkerThreadIOStrategy $ WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:135) 在 org.glassfish.grizzly.threadpool.AbstractThreadPool $ Worker.doWork(AbstractThreadPool.java:564) 在 org.glassfish.grizzly.threadpool.AbstractThreadPool $ Worker.run(AbstractThreadPool.java:544) 在java.lang.Thread.run(Thread.java:745)
答案 0 :(得分:3)
您的班级有ServletContextListener
和websocket EndPoint
。
容器创建一个在应用程序启动时充当ServletContextListener
的实例,并在客户端连接时创建Endpoint
个实例。
现在每个实例都有一个自己的sessions
字段,当你访问它时它仍然是空的(它只在ServletContextListener实例中初始化)。
解决方案:将sessions
字段设为静态(然后解决死锁问题 - 请参阅Andrew Williamson的评论) - 例如,只需使用同步列表。
答案 1 :(得分:0)
在init()
之前永远不会调用此方法
可能init()
真正在open()
之前运行,但状态更改(列表初始化)不一定在运行open()
的其他线程上可见。会话列表初始化不在任何同步块中,因此对于其他线程可能是不可见的。您有以下选择:
ArrayList
实例(或在字段定义中)this
上同步(而不是在sessions
上)this
永远不会为空)