Java 同步对ArrayList的访问
我试图通过并行计算来计算任意数的所有素数。我知道isPrime()可以从现在的情况得到改进,但主要问题是我无法同步对存储结果的ArrayList的访问。我实例化了一个虚拟对象“m”,作为对数组的监视和控制访问。显然,我在推理过程中犯了一个错误,因为每次我都会得到一个java.util.ConcurrentModificationException。注意: 编辑:更新以显示建议后的完成情况Java 同步对ArrayList的访问,java,multithreading,concurrency,locking,concurrentmodification,Java,Multithreading,Concurrency,Locking,Concurrentmodification,我试图通过并行计算来计算任意数的所有素数。我知道isPrime()可以从现在的情况得到改进,但主要问题是我无法同步对存储结果的ArrayList的访问。我实例化了一个虚拟对象“m”,作为对数组的监视和控制访问。显然,我在推理过程中犯了一个错误,因为每次我都会得到一个java.util.ConcurrentModificationException。注意: 编辑:更新以显示建议后的完成情况 import java.util.ArrayList; 公共班机{ 静态最终整数最大值=100000000;
import java.util.ArrayList;
公共班机{
静态最终整数最大值=100000000;
静态final int THREADS=Runtime.getRuntime().availableProcessors();
静态最终int ARRINITSIZE=100000;
静态ArrayList素数=新ArrayList(ARRINITSIZE);
公共静态void main(字符串[]args){
线程[]t=新线程[线程];
PrimeRun.m=新监视器();
对于(inti=0;i将素数声明为
static List<Integer> primes = Collections.synchronizedList(new ArrayList<Integer>(ARRINITSIZE));
static List primes=Collections.synchronizedList(新的ArrayList(ARRINITSIZE));
您应该能够摆脱监视器和同步块…哦,您必须等到计算素数的线程完成后,才能开始打印素数列表中的值
编辑
这里是修改后的程序,它只是等待线程完成
import java.util.ArrayList;
import java.util.List;
public class Main {
static final int MAX = 1000;
static final int THREADS = Runtime.getRuntime().availableProcessors();
static final int ARRINITSIZE = 100000;
static ArrayList<Integer> primes = new ArrayList<Integer>(ARRINITSIZE);
public static void main(String[] args) {
PrimeRun.m = new Monitor();
List<Thread> thread = new ArrayList<Thread>();
for (int i=0; i<THREADS; i++){
Thread t = new Thread(new PrimeRun(i));
t.start();
thread.add(t);
}
for (Thread t : thread) {
if (t.isAlive()){
try {
t.join();
} catch (InterruptedException e) {
}
}
}
for (int n : primes)
System.out.print("" + n + " ");
}
static boolean isPrime(int n) {
if (n <= 1 || (n&1) == 0) return false;
if (n == 2 || n == 3 || n == 5) return true;
for (int i = 3; n*n <= i; i += 2)
if (n % i == 0) return false;
return true;
}
synchronized static void addPrime(int n) {
primes.add(n);
}
}
class PrimeRun implements Runnable {
public static Monitor m;
final int ID;
public PrimeRun(int i) {
ID = i;
}
public void run() {
for(int i=0; i < Main.MAX; i++) {
if(i % Main.THREADS == ID)
if(Main.isPrime(i))
m.addPrime(i);
}
}
}
class Monitor {
public synchronized void addPrime(int n) {
Main.addPrime(n);
}
}
import java.util.ArrayList;
导入java.util.List;
公共班机{
静态最终整数最大值=1000;
静态final int THREADS=Runtime.getRuntime().availableProcessors();
静态最终int ARRINITSIZE=100000;
静态ArrayList素数=新ArrayList(ARRINITSIZE);
公共静态void main(字符串[]args){
PrimeRun.m=新监视器();
列表线程=新的ArrayList();
对于(int i=0;i您会得到ConcurrentModificationException
,因为您在向数组添加时正在对数组进行迭代。这与多个线程无关,甚至会在单个线程中发生。但在您的特定情况下,会发生这种情况,因为当您开始对列表进行迭代时,线程仍在添加素数。为了防止这种情况发生,您必须等待所有线程完成后再打印。为此,您可以迭代线程数组并对每个线程调用join()
。或者您可以使用
列表可以通过集合进行同步。synchronizedList()
如另一个答案中所建议。ConcurrentModificationException的原因是同步不错。原因是您正在对列表进行迭代期间对其进行修改
我在代码中发现的唯一迭代是
for (int n : primes)
System.out.print("" + n + " ");
下面是发生的情况。
您正在运行多个执行其任务并向集合添加元素的线程。运行线程的代码后面紧跟着迭代列表并打印其元素的代码。因此,迭代和您的工作线程同时运行
要解决此问题,应等待所有工作线程完成后再打印。确实,您希望仅在所有计算完成后打印结果。请签出方法Thread.join()
,以实现此功能
显然,要按照@maneesh的建议去做,即使用Collections.synchronizedList()
而不是手动同步。我想了解并发编程的详细信息。否则,我会选择这个。因此,主要来说,应该保留对派生线程的引用,然后加入()请查看编辑后的代码答案,等待线程完成。如果要在迭代列表时尝试添加列表,请先将其转换为数组并迭代数组。要对isPrime()
方法进行注释,而不是除以i+=2,请尝试除以素数。get(i++)。这可以防止你在通过3和5之后被15除。@gobernador你的意思是,读取数组中已经计算过的素数,并用该数字进行模运算?还是说用不同的值增加循环计数器?用小于测试数字的素数进行模运算。这将减少计算并给出y在素数较少的情况下,在更高的数字上,速度有了很大的提高。@gobernador现在的情况是,素数[]没有排序,所以这不会起作用,除非我阻止一些线程,直到每个数字的第n个线程完成,这样它们就按顺序插入了,对吗?我认为顺序并不重要。是否使用素数[]应该可以很好地解决问题已排序或未排序。