Java ArrayList多线程NullPointerException

时间:2016-02-18 20:21:51

标签: java multithreading arraylist

我用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)

2 个答案:

答案 0 :(得分:3)

您的班级有ServletContextListener和websocket EndPoint

容器创建一个在应用程序启动时充当ServletContextListener的实例,并在客户端连接时创建Endpoint个实例。

现在每个实例都有一个自己的sessions字段,当你访问它时它仍然是空的(它只在ServletContextListener实例中初始化)。

解决方案:将sessions字段设为静态(然后解决死锁问题 - 请参阅Andrew Williamson的评论) - 例如,只需使用同步列表。

答案 1 :(得分:0)

  

在init()

之前永远不会调用此方法

可能init()真正在open()之前运行,但状态更改(列表初始化)不一定在运行open()的其他线程上可见。会话列表初始化不在任何同步块中,因此对于其他线程可能是不可见的。您有以下选择:

  • 在类构造函数中创建ArrayList实例(或在字段定义中)
  • 当您访问或创建会话列表时,
  • 始终在this上同步(而不是在sessions上)this永远不会为空)