同步对ArrayList的访问

时间:2012-07-28 05:53:04

标签: java multithreading concurrency locking concurrentmodification

我试图通过并行计算所有素数到任意数。我知道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>

3 个答案:

答案 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()而不是手动同步。