服务器中的空指针异常

Posted

技术标签:

【中文标题】服务器中的空指针异常【英文标题】:Null pointer exception in server 【发布时间】:2012-05-21 20:28:38 【问题描述】:

这是一个客户端-服务器程序。 对于每个客户端,服务器都有一个方法,用于检查是否有一些消息发送到该客户端。

代码:

        while (bool) 
            for(int j = 0;j<Start.bases.size();j++)
                if(Start.bases.get(j).getId() == id)
                    if(!Start.bases.get(j).ifEmpty())
                        String output = Start.bases.get(j).getMessage();
                        os.println(output);
                        System.out.println(output +" *FOT* "+ addr.getHostName());
                    
                

            

每个线程都有一个 id。 所以一切似乎都很好,但是我在这一行得到了奇怪的空指针异常

if(Start.bases.get(j).getId() == id)

id - 整数。 这真的很奇怪,因为我已经在调试这部分运行并检查了“bases”和“id”不为空,并且 bases 有适当的字段。 基数不为空。

顺便说一句,bases 是静态的(因为每个线程都可以使用它),并且在使用此方法之前声明了 bases。

这一行不会引起问题

            for(int j = 0;j<Start.bases.size();j++)

可能是因为getId()方法?

public int getId()
    return id;


有什么问题?

已编辑。

  static ArrayList<Base> bases;
  bases = new ArrayList<Base>();

类基:

 public class Base 
private ServerThread st;
private int id;
private String name;
private ArrayList<String> messages;

public Base(String n, ServerThread s_t, int i_d)
    messages = new ArrayList<String>();
    st = s_t;
    name = n;
    id = i_d;


public String getName()
    return name;

public int getId()
    return id;

public ServerThread getThr()
    return st;

public String getMessage()
    String ret = "";

    if(!messages.isEmpty())
        ret = messages.get(0);
        messages.remove(messages.get(0));
    

    return ret;


public void addMessage(String m)
    messages.add(m);


public boolean ifEmpty()
    return messages.isEmpty();

  

谢谢。

【问题讨论】:

你能告诉我们bases的定义吗? 你能告诉我们错误的堆栈跟踪吗? getId() 返回的字段 id 的确切类型是什么? bases 可能包含 null 值吗? 这可能不会解决你的问题,但你应该像for (Base base : Start.bases) 这样遍历Start.bases,然后将Start.bases.get(j) 处理为base。如果底层集合发生变化,则更清洁且不易出现竞争条件。 请告诉我们确切的错误消息和堆栈跟踪 【参考方案1】:

在这行代码中: (Start.bases.get(j).getId() == id

在这种情况下你可能会有这样的例外:

1) bases is null - you said its wrong
2) bases.get(j) - it may occur only if you collection size was reduced during iteration(as mentioned Gray)
3) Start.bases.get(j).getId() is null. But as you mentioned getId() method return primitive int, so its not the case as in this situation you receive null ponter while casting - in line "    return id;".

所以你应该检查第二种情况。

【讨论】:

不,“j”的值不大于集合的大小 - 1。 @146percentRussian,然后将您的代码笔划分成几部分,以识别在哪个部分出现异常。 现在我在线程“Thread-1”java.util.ConcurrentModificationException 中得到异常 @146percentRussian,上面提到了一些东西——几个线程同时改变了你的收藏。将所有读/写操作放在同步块中或使用concurrentHashMap或HashTable等线程保存集合。 将所有调用放入如下块中: synchronized(Start.bases) Start.bases.add(...) 或 synchronized(Start.bases) Start.bases.get(.. .)。但这不是最好的解决方案。【参考方案2】:

鉴于此:

“我已经在调试这部分运行并检查了“bases”和“id”不为空,并且 bases 有适当的字段”

还有这个:

bases 是静态的(因为每个线程都可以使用它)

我认为你很可能有竞争条件。在竞争条件下,有两个线程同时访问同一个数据结构(在本例中为 Start.bases)。大多数情况下,一个线程的代码完成得更快,一切都按照您的预期进行,但有时另一个线程会抢占先机或运行得比平时快一点,然后事情就会“繁荣”。

当您引入带有断点的调试器时,您几乎保证带有断点的代码将最后执行,因为您在所有其他线程都在执行时停止了它还在继续。

我建议您的列表大小可能会随着您的执行而改变。当用户离开时,他们的条目是否从“基本”列表中删除?是否有其他情况可以在执行期间从另一个线程更改列表?

我建议的第一件事是您将代码切换为使用迭代器而不是直接的“for”循环。它不会让问题消失(它实际上可能使它更明显),但它会使正在发生的事情更加清晰。 在修改发生的时候你会得到一个 ConcurrentModificationException,而不是只有在发生某些更改组合时才有用的 NullPointerException。):

        for(Base currentBase : Start.bases)
        
            if(currentBase.getId() == id && !currentBase.ifEmpty())
            
                String output = currentBase.getMessage();
                os.println(output);
                System.out.println(output +" *FOT* "+ addr.getHostName());
            
        

如果您确实在上面的代码中遇到了并发修改异常,那么您肯定是在处理竞争条件。这意味着您必须同步您的代码。

有几种方法可以做到这一点,具体取决于您的应用程序的结构。

假设竞争只在这段代码和另一段代码之间进行(从列表中删除的部分),您可能可以通过包装这两个代码块来解决 this 场景在

synchronized(Start.bases)

   [your for-loop/item removal code goes here]

这将在列表本身上获得一个锁,这样这两段代码就不会尝试在不同的线程中同时更新同一个列表。 (请注意,它不会停止对 Base 对象本身的并发修改,但我怀疑这就是这种情况下的问题)。

综上所述,任何时候你有一个变量可以被多个线程读/写访问,它真的应该被同步。这是一项相当复杂的工作。如果可以的话,最好将同步保持在您正在管理的对象内部。这样您就可以在一个地方查看所有同步代码,从而减少意外造成死锁的可能性。 (在上面的代码中,您需要在 Start 类中创建“for”循环以及使用该列表的任何其他方法,然后将“bases”设为私有,以便应用程序的其余部分必须使用这些方法)。

没有看到代码中访问此列表的所有其他位置,我无法确切说明您应该进行哪些更改,但希望这足以让您开始。请记住,Java 中的多线程需要非常精细的手!

【讨论】:

是的!添加同步后,我的服务器开始正常工作!非常感谢

以上是关于服务器中的空指针异常的主要内容,如果未能解决你的问题,请参考以下文章

服务类Spring引导中的空指针异常[重复]

如何解决spring boot test中的空指针异常?

在检查空值时 onPostExecute 中的 AsyncTask 中的空指针异常

RestEasy 异步请求中的空指针异常

RestEasy 异步请求中的空指针异常

onPostExecute 中的空指针异常