Java 个具有 ConcurrentModificationException 的线程

Java Threads with ConcurrentModificationException


我目前正在开发我的第一个多线程软件——一个计算素数的程序……
基本上我创建了 n(线程数)runnable。这些可运行对象被添加到一个 ArrayList 中。他们检查一个数字是否是质数。如果数字是质数,我将它添加到一个长数组中以备后用。因为我希望素数在这个数组中以正确的顺序排列,所以我需要特定的线程来等待其他线程。我通过遍历 ArrayList(见上文)并等待检查较低数字的线程来执行此操作。
线程完成后,我想将它从给定的 ArrayList 中删除,但我不能,因为其他线程仍在循环它(我猜这就是发生 ConcurrentModificationException 的原因 - 这是我第一次使用线程...... ).


真心希望大家能帮帮我:)
非常感谢!

马蒂亚斯

我的runnable class(我只是在main方法class中创建了四个对象):

导入java.util.ArrayList;

public class PrimeRunnable implements Runnable {

    //Static Util
    public static ArrayList<PrimeRunnable> runningThreads = new ArrayList<PrimeRunnable>();
    public static long[] primes;
    public static int nextFreeIndex = 1;
    public static long nextPossiblePrime = 3;

    //Object specific
    private long numberToCheck;
    private Thread primeThread;
    private String threadName;
    private long threadID;

    public PrimeRunnable() {
        numberToCheck = nextPossiblePrime;
        increaseNextPossiblePrime();

        threadName = "ThreadToCheck" + numberToCheck;
        threadID = numberToCheck;

        runningThreads.add(this);
    }

    @Override
    public void run() {
        boolean isPrime = true;
        double sqrtOfPossiblePrime = Math.sqrt(numberToCheck);

        long lastDevider = 0;

        for(int index = 0; index < nextFreeIndex; index++) {
            lastDevider = primes[index];
            if(numberToCheck%primes[index] == 0) {
                isPrime = false;
                break;
            }
            if(primes[index] > sqrtOfPossiblePrime) {
                break;
            }
        }

        while(lastDevider < sqrtOfPossiblePrime) {
            lastDevider += 1;

            if(numberToCheck%lastDevider == 0) {
                isPrime = false;
                break;
            }
        }

        if(isPrime) {
            //Wait for lower Threads.

            for(PrimeRunnable runnable : runningThreads) {
                if(runnable.getThreadID() < this.getThreadID()) {
                    try {
                        runnable.primeThread.join();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }

            primes[nextFreeIndex] = numberToCheck;
            increaseNextFreeIndex();
            System.out.println(numberToCheck);
        }
        runningThreads.remove(this);
    }

    public void start() {
        if(primeThread == null) {
            primeThread = new Thread(this, threadName);
        }

        primeThread.start();
    }

    public void reset() {
        numberToCheck = nextPossiblePrime;
        increaseNextPossiblePrime();

        threadName = "ThreadToCheck" + numberToCheck;
        threadID = numberToCheck;

        //No need to readd into runningThread, since we only manipulate an already existing object.
        primeThread = new Thread(this, threadName);
        primeThread.start();
    }

    public static void setUpperBorder(int upperBorder) {
        if(primes == null) {
            primes = new long[upperBorder];
            primes[0] = 2;
        } else {
            System.err.println("You are not allowed to set the upper border while running.");
        }
    }

    public long getNumberToCheck() {
        return numberToCheck;
    }

    private void increaseNextPossiblePrime() {
        nextPossiblePrime += 2;
    }

    private void increaseNextFreeIndex() {
        nextFreeIndex += 2;
    }

    public long getThreadID() {
        return threadID;
    }

    public boolean isAlive() {
        return primeThread.isAlive();
    }
}

包含同步方法 publishPrimePrimeListener class 将素数插入列表中的正确位置怎么样?如果从 LinkedList.

的最后一个索引开始,则在列表的正确位置插入应该不会花费太多时间

或者,您也可以将其插入 SortedSet(实现:TreeSet)。我想你无论如何都不想要任何重复的素数。在这种情况下,可以直接使用 synchronizedSortedSet 而不是侦听器。

请注意,您似乎仍然停留在较低级别的结构上。当在 Java 上并发编程时,使用更高级别的结构(执行器、期货、并发队列等)是值得的。

The main distinction between fail-fast and fail-safe iterators is whether or not the collection can be modified while it is being iterated. Fail-safe iterators allow this; fail-fast iterators do not.

Fail-fast iterators operate directly on the collection itself. During iteration, fail-fast iterators fail as soon as they realize that the collection has been modified (i.e., upon realizing that a member has been added, modified, or removed) and will throw a ConcurrentModificationException. Some examples include ArrayList, HashSet, and HashMap (most JDK1.4 collections are implemented to be fail-fast). Fail-safe iterates operate on a cloned copy of the collection and therefore do not throw an exception if the collection is modified during iteration. Examples would include iterators returned by ConcurrentHashMap or CopyOnWriteArrayList.

我能够使用 Java 实施并发列表 CopyOnWriteArrayList

重现并修复该问题

这是我的主要内容class

public class PrimeRunnableMain {

    public static void main(String[] args) {
        PrimeRunnable.setUpperBorder(10);
        PrimeRunnable primeRunnable1 = new PrimeRunnable();
        PrimeRunnable primeRunnable2 = new PrimeRunnable();
        PrimeRunnable primeRunnable3 = new PrimeRunnable();
        PrimeRunnable primeRunnable4 = new PrimeRunnable();
        primeRunnable1.start();
        primeRunnable2.start();
        primeRunnable3.start();
        primeRunnable4.start();
    }
}

这是 PrimeRunnable

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class PrimeRunnable implements Runnable {

    // Static Util
    public static List<PrimeRunnable> runningThreads = new CopyOnWriteArrayList<PrimeRunnable>();
    public static long[] primes;
    public static int nextFreeIndex = 1;
    public static long nextPossiblePrime = 3;

    // Object specific
    private long numberToCheck;
    private Thread primeThread;
    private String threadName;
    private long threadID;

    public PrimeRunnable() {
        numberToCheck = nextPossiblePrime;
        increaseNextPossiblePrime();

        threadName = "ThreadToCheck" + numberToCheck;
        threadID = numberToCheck;

        runningThreads.add(this);
    }

    @Override
    public void run() {
        boolean isPrime = true;
        double sqrtOfPossiblePrime = Math.sqrt(numberToCheck);

        long lastDevider = 0;

        for (int index = 0; index < nextFreeIndex; index++) {
            lastDevider = primes[index];
            if (numberToCheck % primes[index] == 0) {
                isPrime = false;
                break;
            }
            if (primes[index] > sqrtOfPossiblePrime) {
                break;
            }
        }

        while (lastDevider < sqrtOfPossiblePrime) {
            lastDevider += 1;

            if (numberToCheck % lastDevider == 0) {
                isPrime = false;
                break;
            }
        }

        if (isPrime) {
            // Wait for lower Threads.

            for (PrimeRunnable runnable : runningThreads) {
                if (runnable.getThreadID() < this.getThreadID()) {
                    try {
                        runnable.primeThread.join();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }

            primes[nextFreeIndex] = numberToCheck;
            increaseNextFreeIndex();
            System.out.println(numberToCheck);
        }
        runningThreads.remove(this);
    }

    public void start() {
        if (primeThread == null) {
            primeThread = new Thread(this, threadName);
        }

        primeThread.start();
    }

    public void reset() {
        numberToCheck = nextPossiblePrime;
        increaseNextPossiblePrime();

        threadName = "ThreadToCheck" + numberToCheck;
        threadID = numberToCheck;

        // No need to readd into runningThread, since we only manipulate an
        // already existing object.
        primeThread = new Thread(this, threadName);
        primeThread.start();
    }

    public static void setUpperBorder(int upperBorder) {
        if (primes == null) {
            primes = new long[upperBorder];
            primes[0] = 2;
        } else {
            System.err
                    .println("You are not allowed to set the upper border while running.");
        }
    }

    public long getNumberToCheck() {
        return numberToCheck;
    }

    private void increaseNextPossiblePrime() {
        nextPossiblePrime += 2;
    }

    private void increaseNextFreeIndex() {
        nextFreeIndex += 2;
    }

    public long getThreadID() {
        return threadID;
    }

    public boolean isAlive() {
        return primeThread.isAlive();
    }
}