Java 引发CurrentModificationException的同步线程
我在编写的一个简单的UDP聊天客户端上运行并发时遇到了问题,在我尝试将新联系人添加到存储在Peer中的传出消息列表之前,这一切都可以正常工作,它会抛出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
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是查看一些棘手问题的好方法