Java 引发CurrentModificationException的同步线程

Java 引发CurrentModificationException的同步线程,java,multithreading,synchronized,Java,Multithreading,Synchronized,我在编写的一个简单的UDP聊天客户端上运行并发时遇到了问题,在我尝试将新联系人添加到存储在Peer中的传出消息列表之前,这一切都可以正常工作,它会抛出CurrentModificationException,有人能帮我了解哪里出了问题吗 这是我的课 import java.net.*; import java.util.*; import java.io.*; public class Chatter { public static class ReceiveMess exte

我在编写的一个简单的UDP聊天客户端上运行并发时遇到了问题,在我尝试将新联系人添加到存储在Peer中的传出消息列表之前,这一切都可以正常工作,它会抛出CurrentModificationException,有人能帮我了解哪里出了问题吗

这是我的课

    import java.net.*;
import java.util.*;
import java.io.*;

public class Chatter {

   public static class ReceiveMess extends Thread{ 

   DatagramSocket ds;   
   public ReceiveMess(DatagramSocket s){
        ds = s;
    } 

     byte[] Rbuf = new byte[1000];
     DatagramPacket Rdgp = new DatagramPacket(Rbuf, Rbuf.length); 

        public synchronized void run() {
            try{
                while (true){
                    for(Peer p : Peer.PeerList){
                        ds.receive(Rdgp);
                        String rcvd = new String(Rdgp.getData(), 0, Rdgp.getLength()) + ", from address: "
                        + Rdgp.getAddress() + ", port: " + Rdgp.getPort();
                        System.out.println(rcvd);
                    }
                }
            }
            catch(IOException e) {
                System.out.println(e);
            }
        }
    }

    public static class SendMess extends Thread{

    DatagramSocket ds;  

    public SendMess(DatagramSocket s){
            ds = s;
    }


    int SPORT = 40080;
    byte[] Sbuf = new byte[1000];
    DatagramPacket Sdgp = new DatagramPacket(Sbuf, Sbuf.length);

        public synchronized void run() {
            try{
                while (true) {
                    BufferedReader consR = new BufferedReader(new InputStreamReader(System.in));
                    String MessOut = consR.readLine();
                    if(MessOut.startsWith("/NEW")){
                        try{
                            String[] splitArray = MessOut.split(" ");
                            String newIP = (splitArray[1]);
                            Peer p = new Peer(newIP);
                            System.out.println(newIP + " added to the contacts list");
                            continue;
                        }
                        catch(Exception e){
                            System.out.println("Please format NEW IP address's as NEW XXX.XXX.XXX.XXX");
                            continue;
                        }
                    }
                    else{
                        Sbuf = ("Server Said: " + MessOut).getBytes();
                        for(Peer p : Peer.PeerList){
                            DatagramPacket out = new DatagramPacket(Sbuf, Sbuf.length, p.IP, SPORT);
                        ds.send(out);}
                    }
                }
            }
            catch(IOException e) {
                System.out.println(e);
            }
        }
    }

    public static void main(String [] args){

      try{  
        for(String s : args){
            String address = s;
            Peer peer = new Peer(address);
        }

      int PORT = 40080;
      DatagramSocket ds = new DatagramSocket(PORT);

      Peer.PrintList();    

        SendMess sendmess = new SendMess(ds);
        sendmess.start();        
        ReceiveMess receivemess = new ReceiveMess(ds);
        receivemess.start();  
    }
    catch(Exception e){
        System.out.println(e);
    }
    }
}

And my peer class,

        import java.net.*;
    import java.util.*;

    public class Peer{



        InetAddress IP;
        static List<Peer> PeerList = new LinkedList<Peer>();

        Peer(String clientAddress){ 
            try{
                IP = IP.getByName(clientAddress);
                AddToList(this);
            }
            catch(UnknownHostException e){
                System.out.println(e.toString());
            }
        }   

        public synchronized void AddToList(Peer peer){
            PeerList.add(this);
        }

        public List<Peer> GetList(){
            return PeerList;
        }

        public static void PrintList(){
            for(Peer p : PeerList){
            System.out.println(p.IP.toString());
            }   
        }
    }
import java.net.*;
导入java.util.*;
导入java.io.*;
公共类聊天{
公共静态类ReceiveMess扩展线程{
数据采集器ds;
公共收据(DatagramSocket s){
ds=s;
} 
字节[]Rbuf=新字节[1000];
DatagramPacket Rdgp=新的DatagramPacket(Rbuf,Rbuf.length);
公共同步的无效运行(){
试一试{
while(true){
for(Peer p:Peer.PeerList){
ds.receive(Rdgp);
String rcvd=新字符串(Rdgp.getData(),0,Rdgp.getLength())+“,来自地址:”
+Rdgp.getAddress()+”,端口:“+Rdgp.getPort();
系统输出打印(rcvd);
}
}
}
捕获(IOE异常){
系统输出打印ln(e);
}
}
}
公共静态类SendMess扩展线程{
数据采集器ds;
公共发送消息(DatagramSocket s){
ds=s;
}
int SPORT=40080;
字节[]Sbuf=新字节[1000];
DatagramPacket Sdgp=新的DatagramPacket(Sbuf,Sbuf.length);
公共同步的无效运行(){
试一试{
while(true){
BufferedReader consR=新的BufferedReader(新的InputStreamReader(System.in));
字符串MessOut=consR.readLine();
if(MessOut.startsWith(“/NEW”)){
试一试{
String[]splitArray=MessOut.split(“”);
字符串newIP=(splitArray[1]);
Peer p=新的Peer(newIP);
System.out.println(newIP+“添加到联系人列表”);
继续;
}
捕获(例外e){
System.out.println(“请将新的IP地址格式化为新的XXX.XXX.XXX.XXX”);
继续;
}
}
否则{
Sbuf=((“服务器说:”+MessOut).getBytes();
for(Peer p:Peer.PeerList){
DatagramPacket out=新的DatagramPacket(Sbuf、Sbuf.length、p.IP、SPORT);
发送(出去);}
}
}
}
捕获(IOE异常){
系统输出打印ln(e);
}
}
}
公共静态void main(字符串[]args){
试试{
for(字符串s:args){
字符串地址=s;
对等=新对等(地址);
}
int端口=40080;
DatagramSocket ds=新的DatagramSocket(端口);
Peer.PrintList();
SendMess SendMess=新的SendMess(ds);
sendmass.start();
ReceiveMess ReceiveMess=新的ReceiveMess(ds);
receivemess.start();
}
捕获(例外e){
系统输出打印ln(e);
}
}
}
还有我的同班同学,
导入java.net。*;
导入java.util.*;
公共类同级{
IP地址;
静态列表PeerList=newlinkedlist();
对等(字符串客户端地址){
试一试{
IP=IP.getByName(客户端地址);
AddToList(本);
}
捕获(未知后异常e){
System.out.println(例如toString());
}
}   
公共同步的void AddToList(对等){
PeerList.add(这个);
}
公共列表GetList(){
返回PeerList;
}
公共静态void打印列表(){
用于(对等p:PeerList){
System.out.println(p.IP.toString());
}   
}
}

方法的关键字
synchronized
相当于:

public void run() {
    synchronized(this) {
     ...
    }
}
在同步方法中,
是一个不同的对象(
ReceiveMess
sendmmess
Peer
),因此没有任何东西可以阻止这些方法同时运行。改用一个普通监视器,(
Chatter.class
是最方便的方法)


换句话说,
synchronized
for methods不是全局的,它只是在
this
的监视器上进行同步的快捷方式。我鼓励你至少在继续之前阅读。

所以你的问题是

ReceiveMess
中有一个循环,它在
Peer
中迭代列表。同时,在
sendmass
中,您正在创建
Peer
的新实例。当创建了
Peer
的新实例时,将向
PeerList
添加一个新元素。这将导致CME进入
ReceiveMess

我建议您删除所有
synchronized
关键字,因为它们没有为您做任何事情,并从
Peer.getList
返回列表副本。这意味着,当一个线程正在迭代列表时,即使另一个线程修改了
对等线程的列表,迭代线程也不会受到影响。迭代线程将在while循环的下一次迭代中看到更新

一种机制是使用
CopyOnWriteArrayList
或Guava的
ImmutableList.Builder


在代码的哪一行出现异常?请添加您需要在共享对象上同步的StackTrace,否则它实际上什么都做不了。扩展线程被认为是不好的做法,在其中使用synchronized是查看一些棘手问题的好方法