![](/img/trans.png)
[英]ConcurrentModificationException in multithread Server/Client chat application
[英]Multithread ConcurrentModificationException
我已经在网上搜索了一段时间,试图解决此问题,但没有成功。
在我的应用程序中,我尝试使用基本的可交换加密方案加密大量消息。 由于集合中包含大量的BigInteger,因此我尝试对加密进行多线程处理以提高性能。
基本上,我收集大量消息并将其拆分为多个子集,这些子集传递给加密线程以进行加密的子集。 然后,我尝试提取每个子集,并在线程全部完成各自的工作之后将它们聚合为原始的大集合。 当我遍历线程并提取它们的每种加密时,当我尝试将所有加密实际添加到所有加密列表中时发生错误,并且抛出的错误是java.util.ConcurrentModificationException错误。
我试图使用同步,但没有帮助。
这是函数调用:
protected Set<BigInteger> multiEncrypt(BigInteger key, HashSet<BigInteger> messageSet) {
ArrayList<BigInteger> messages = new ArrayList<BigInteger>(messageSet);
Set<BigInteger> encryptions = Collections.synchronizedSet(new HashSet<BigInteger>());
int cores = Runtime.getRuntime().availableProcessors();
int numMessages = messages.size();
int stride = numMessages/cores;
//create all the threads and run them
ArrayList<EncryptThread> threads = new ArrayList<EncryptThread>();
for (int thread = 0; thread < cores; thread++) {
int start = thread*stride;
//don't want to go over the end
int stop = ((thread+1)*stride >= messages.size()) ? messages.size()-1 : (thread+1)*stride;
List<BigInteger> subList = messages.subList(start, stop);
EncryptThread t = new EncryptThread(encryptionScheme.getPrime(), key, subList);
t.start();
threads.add(t);
}
//pull out the encryptions
synchronized(encryptions){
for (int i=0; i < threads.size()-1; i++) {
EncryptThread thread = threads.get(i);
ArrayList<BigInteger> these = thread.getEncryptions();
encryptions.addAll(these); //<-- Erroring Here
thread.finish();
}
}
这是我为执行加密而编写的EncryptThread类的相关部分:
/**
* Constructor
*/
public EncryptThread(BigInteger prime, BigInteger key, List<BigInteger> messages) {
//need a new encryption scheme object for each thread
encryptionScheme = new EncryptionScheme(prime);
encryptions = new ArrayList<BigInteger>();
this.key = key;
this.messages = messages;
wait = true;
}
@Override
public void run() {
encryptMessages(key, messages);
while(wait);
}
/**
* Used to encrypt a set of messages
* @param key
* @param messages
* @return
*/
public void encryptMessages(BigInteger key, List<BigInteger> messages) {
System.out.println("Encrypting stuff");
for (BigInteger m : messages) {
BigInteger em = encryptionScheme.encrypt(key, m);
encryptions.add(m);
}
}
public ArrayList<BigInteger> getEncryptions() {
return encryptions;
}
//call this after encryptions have been pulled to let the thread finish
public void finish() {
wait = false;
}
}
我不是Java的新手,但是我是Java多线程的新手,所以我将不胜感激。 提前致谢!
编辑:根据建议,我向EncryptThread类添加了一个简单的锁定机制,该机制使线程等待返回加密,直到它们全部完成并且现在可以使用。
public void encryptMessages(BigInteger key, List<BigInteger> messages) {
System.out.println("Encrypting stuff");
this.lock = true;
for (BigInteger m : messages) {
BigInteger em = encryptionScheme.encrypt(key, m);
//deals with when we have to mark chaff at S2
if (shift) {
em.shiftLeft(1);
if(shiftVal != 0) em.add(BigInteger.ONE);
}
encryptions.add(m);
}
this.lock = false;
}
public ArrayList<BigInteger> getEncryptions() {
while(lock);
return encryptions;
}
编辑#2因此,我最终使用了实验室的某人向我建议的解决方案。 我摆脱了锁和等待布尔值以及EncryptThread类中的finish()函数,而是在start和getEncryption循环之间添加了一个简单的thread.join()循环:
//create all the threads
ArrayList<EncryptThread> threads = new ArrayList<EncryptThread>();
for (int thread = 0; thread < cores; thread++) {
int start = thread*stride;
//don't want to go over the end
int stop = ((thread+1)*stride >= messages.size()) ? messages.size()-1 : (thread+1)*stride;
List<BigInteger> subList = messages.subList(start, stop);
EncryptThread t = new EncryptThread(encryptionScheme.getPrime(), key, subList, shiftVal);
t.start();
threads.add(t);
}
//wait for them to finish
for( EncryptThread thread: threads) {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//pull out the encryptions
for (int i=0; i < threads.size()-1; i++) {
EncryptThread thread = threads.get(i);
encryptions.addAll(thread.getEncryptions());
}
我认为我的主要困惑是,我认为线程类在完成运行后无法调用其方法。 但以上工作正常。
如果查看List
的addAll上的文档, 它将显示以下内容 :
按照指定集合的迭代器返回的顺序,将指定集合中的所有元素追加到此列表的末尾(可选操作)。 如果在操作进行过程中修改了指定的集合,则此操作的行为是不确定的。 (请注意,如果指定的集合是此列表,并且是非空的,则将发生这种情况。)
您可以看到,在addAll
在使用encryptMessages
方法中的迭代器进行修改的同时,列表正在被修改,您所生成的线程之一当前正在执行。
for (BigInteger m : messages) {
BigInteger em = encryptionScheme.encrypt(key, m);
encryptions.add(m); // <-- here
}
我没有完全浏览所有代码,但是这里的某些内容不是线程安全的。 您最好使用CopyOnWriteArrayList
而不是常规的ArrayList
来避免ConcurrentModificationException
,即如果可以将所有内容都没有添加到addAll调用中的列表中,就可以了,否则,您还需要等待线程完成。 您可能只想将任务与ExecutorService一起使用。 可能还有其他改进。
另外,每个人都提到要学习如何在Java中编写线程安全程序的入门书是《实践中的并发性》,如果您不熟悉Java的并发性,我建议您。
当您在对Collection进行迭代时修改Collection时,会发生ConcurrentModificationException。 它与多线程关系不大,因为您可以轻松创建单线程示例:
ArrayList<String> myStrings = new ArrayList<>();
myStrings.add("foo");
myStrings.add("bar");
for(String s : myStrings) {
myStrings.add("Hello ConcurrentModificationException!");
您将在此处启动线程。
for (int thread = 0; thread < cores; thread++) {
int start = thread*stride;
//don't want to go over the end
int stop = ((thread+1)*stride >= messages.size()) ? messages.size()-1 : (thread+1)*stride;
List<BigInteger> subList = messages.subList(start, stop);
EncryptThread t = new EncryptThread(encryptionScheme.getPrime(), key, subList);
t.start();
threads.add(t);
}
好。 然后,您必须等待所有线程完成,然后才能在此块中开始聚合。
//pull out the encryptions
synchronized(encryptions){
for (int i=0; i < threads.size()-1; i++) {
EncryptThread thread = threads.get(i);
ArrayList<BigInteger> these = thread.getEncryptions();
encryptions.addAll(these); //<-- Erroring Here
thread.finish();
}
}
您正在阻止仅访问encryptions
线程。 但您创建的线程无法访问set
。 平均时间也将继续加入到自己的数组列表these
。 因此,当您调用encryptions.addAll(these);
这两个线程可以访问它们(拥有encryptions
的线程和拥有these
encryptions
的线程
其他答案提供了有关为何在addAll中并发异常的详细信息。
您必须等待所有线程完成工作。
您可以使用ExecutorService
进行此操作
将您的起始线程更改为
ExecutorService es = Executors.newFixedThreadPool(cores);
for(int i=0;i<5;i++)
es.execute(new Runnable() { /* your task */ }); //EncryptThread instance
es.shutdown();
boolean finshed = es.awaitTermination(1, TimeUnit.MINUTES);
然后处理您的回加过程。
ExecutorService es = Executors.newFixedThreadPool(cores);
for (int thread = 0; thread < cores; thread++) {
int start = thread*stride;
//don't want to go over the end
int stop = ((thread+1)*stride >= messages.size()) ? messages.size()-1 : (thread+1)*stride;
List<BigInteger> subList = messages.subList(start, stop);
EncryptThread t = new EncryptThread(encryptionScheme.getPrime(), key, subList);
es.execute(t);
threads.add(t);
}
es.shutdown();
boolean finshed = es.awaitTermination(1, TimeUnit.MINUTES);
//pull out the encryptions
synchronized(encryptions){
for (int i=0; i < threads.size()-1; i++) {
EncryptThread thread = threads.get(i);
ArrayList<BigInteger> these = thread.getEncryptions();
encryptions.addAll(these); //<-- Erroring Here
thread.finish();
}
}
假设您的EncryptThread现在是Thread。 您可能需要更改以实现Runnable。 在getEncryptions中没有其他更改
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.