我试图通过并行计算所有素数到任意数。我知道isPrime()可以从现在改进,但主要问题是我不能同步访问存储结果的ArrayList。我已经实例化了一个虚拟对象“m”,用作监视器并控制对数组的访问。显然我在某处推理时犯了一个错误,因为每次都会得到一个java.util.ConcurrentModificationException。 NB:gist
编辑:更新以显示建议后的完成情况。
import java.util.ArrayList;
public class Main {
static final int MAX = 100000000;
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) {
Thread[] t = new Thread[THREADS];
PrimeRun.m = new Monitor();
for (int i=0; i<THREADS; i++) {
t[i] = new Thread(new PrimeRun(i) );
t[i].start();
}
// wait for threads to finish
for (int i=0; i<THREADS; i++)
t[i].join();
// NOTE: primes will be out of order because of random thread scheduling
for (int n : primes)
System.out.print("" + n + " ");
System.out.println();
}
static boolean isPrime(int n) {
if (n == 2 || n == 3 || n == 5) return true;
if (n <= 1 || (n&1) == 0) return false;
for (int i = 3; i*i <= n; 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);
}
}
为方便起见,我已经包含了我的蚂蚁脚本:)
<project default="cmp">
<target name="cmp"><javac srcdir="." debug="true"/></target>
<target name="run" depends="cmp"><java classname="Main" classpath="."/></target>
<target name="cln"><delete><fileset dir="." includes="*.class"/></delete></target>
</project>
答案 0 :(得分:4)
如何将素数声明为
static List<Integer> primes = Collections.synchronizedList(new ArrayList<Integer>(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);
}
}
答案 1 :(得分:4)
你得到ConcurrentModificationException
,因为你在添加数组时迭代数组。它与多线程无关,即使在单个线程中也会发生。但是在您的特定情况下会发生这种情况,因为当您开始遍历列表时,线程仍在添加素数。为防止在打印前必须等待所有线程完成。为此,您可以迭代线程数组并在每个线程上调用join()
。或者您可以使用CountdownLatch。
可以通过Collections.synchronizedList()
按照其他答案的建议使列表同步。
答案 2 :(得分:1)
来自ConcurrentModificationException
的原因同步不错。原因是您在迭代期间修改列表。
我在代码中找到的唯一迭代是
for (int n : primes)
System.out.print("" + n + " ");
以下是发生的事情。 您正在运行多个执行其工作的线程并将元素添加到集合中。运行线程的代码紧跟在迭代列表并打印其元素的代码之后。所以迭代和你的工作线程同时运行。
要解决此问题,您应该等到所有工作线程完成后再打印。实际上,只有在所有计算完成后才打印结果。查看方法Thread.join()
以实现此目的。
显然要做@maneesh推荐给你的东西,即使用Collections.synchronizedList()
而不是手动同步。