服务器中的空指针异常

时间:2012-05-21 20:28:38

标签: java multithreading nullpointerexception

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

代码:

        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”不是null并且base有适当的字段。 基地不是空的。

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

此行不会导致问题

            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();
}
  }

感谢。

2 个答案:

答案 0 :(得分:2)

在这行代码中:     (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;".

所以你应该检查第二个案例。

答案 1 :(得分:1)

鉴于此:

“我已在调试此部分中运行,并检查”bases“和”id“不为null且base具有适当的字段”

和此:

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

我认为你很可能有竞争条件。在竞争条件下,有两个线程同时访问相同的数据结构(在本例中为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());
            }
        }

如果使用上面的代码获得并发修改异常,那么你肯定是在处理竞争条件。这意味着您必须同步代码。

有两种方法可以执行此操作,具体取决于应用程序的结构。

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

synchronized(Start.bases)
{
   [your for-loop/item removal code goes here]
}

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

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

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