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

码:

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


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

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

假设竞争仅发生在这段代码和其他代码之间(执行从列表中删除的部分),则可以通过将两个代码块都包装在

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


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

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

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

07-24 20:58