【发布时间】:2014-04-11 15:58:03
【问题描述】:
我已经在网上搜索了一段时间试图解决这个问题,但没有成功。
在我的应用程序中,我尝试使用基本交换加密方案对大量消息进行加密。由于这些集合是大量的 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;
}
EDIT #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());
}
我认为我的主要困惑是我认为线程类在完成运行后不能调用它的方法。但上述工作正常。
【问题讨论】:
-
你的 //pull out 加密应该在所有线程完成他们的进程后运行。是这样吗?如果任何一个线程正在将消息添加到它自己的数组列表中,并且您尝试在此处添加相同的列表。这导致了这个问题。所有线程完成后运行聚合部分。
-
啊,明白了。我确保线程在 getEncryptions 方法返回之前完成加密并修复它,谢谢!
-
@user2666216 您当前的轮询解决方案可能不是最好的。您可能希望使用 ExecutorService 将线程转换为任务并等待它们完成。这段代码可能仍然不是线程安全的。
-
@user2666216 我同意 NESPowerGlove 。使用 ExecutorService。我也确实在我的回答中提到过
-
虽然 ExecutorService 似乎是一个合理的解决方案,但我最终得到的解决方案对我来说似乎更直观。不过感谢您的建议,我真的很感激。
标签: java multithreading concurrentmodification