简体   繁体   中英

Getting concurrent modification exception on multi threaded merge

Basically I have this code that is using multiple threads to execute a merge sort algorithm I hope to scale this up to N given threads but currently I'm just trying to get four to work. Basically I create four different threads and pass each of them a subarray of the whole. After they've executed I have 4 different sorted sub-arrays that I then need to merge. Because I'm not really sure how to close threads and clear those resources entirely I am trying to reuse two of those threads to absorb a given array into their internal arrays and then re-run the thread with a boolean which tells the thread to merge the two halves rather than sort everything again. This seems to work the first time when I try with merger 0, and merger 1, but then when I try to do the same thing with 2 and three I get a concurrent modification exception. Now I'm not really sure what I'm doing wrong and if anyone has suggestions on how I could improve this code or reduce the number of array creations and copies that would be greatly appreciated.

import java.util.ArrayList;
import java.util.List;

public class RecursiveSimples {

    public static void main(String[] args) throws InterruptedException {
        List<Comparable> nums = new ArrayList<Comparable>();
        nums.add(7); nums.add(4);
        nums.add(8); nums.add(6);
        nums.add(1); nums.add(3);
        nums.add(4); nums.add(7);
        nums.add(2); nums.add(1);
        nums.add(5); nums.add(9);
        nums.add(8); nums.add(3);
        nums.add(2); nums.add(2);

        System.out.println(Runtime.getRuntime().availableProcessors());
        int r = nums.size() % 4;
        int num = nums.size() / 4;

        List<Merger> mergers = new ArrayList<Merger>();
        mergers.add(new Merger(nums.subList(0, num))); 
        mergers.add(new Merger(nums.subList(num, num*2)));
        mergers.add(new Merger(nums.subList(num*2, num*3))); 
        mergers.add(new Merger(nums.subList(num*3, (num*4) +r)));

        mergers.get(0).start(); mergers.get(1).start();
        mergers.get(2).start(); mergers.get(3).start();

        mergers.get(0).join(); mergers.get(1).join();
        mergers.get(2).join(); mergers.get(3).join();

        System.out.println(mergers.get(0).getNums());
        System.out.println(mergers.get(1).getNums());
        System.out.println(mergers.get(2).getNums());
        System.out.println(mergers.get(3).getNums());

        mergers.get(0).absorbList(mergers.get(1).getNums());
        mergers.get(0).setMerger(true);
        mergers.get(0).run();
        System.out.println(mergers.get(0).getNums());

        mergers.get(2).absorbList(mergers.get(3).getNums());
        mergers.get(2).setMerger(true);
        mergers.get(2).run();

        System.out.println(mergers.get(1).getNums());
        System.out.println(mergers.get(3).getNums());

        int maxThreads = nums.size() / 2;



    }
}


class Merger extends Thread {
    private List<Comparable> nums;
    private boolean merge = false;

    public List<Comparable> getNums() {
        return nums;
    }

    public void setMerger(boolean bool) {
        merge = bool;
    }

    public void absorbList(List<Comparable>  list) {
        nums.addAll(list);
    }

    Merger(List<Comparable> arr) {
        nums = arr;
    }

    public void run() {
        if(merge == false) {
            mergeSort(nums, 0, nums.size() -1);
        }else {
            merge(nums, 0, (nums.size() -1)/2, nums.size() -1);
        }
    }

    public static void swap(List<Comparable> nums, int index1, int index2)
    {
        Comparable temp;
        temp = nums.get(index1);
        nums.set(index1, nums.get(index2));
        nums.set(index2, temp);
    }

    private static void mergeSort(List<Comparable> nums, int first, int last) {
         if (first < last)
            {
                int m = (first+last)/2;

                mergeSort(nums, first, m);
                mergeSort(nums, m+1, last);

                merge(nums, first, m, last);
            }
    }

    private static void merge(List<Comparable> nums, int first, int mid, int last){
         List<Comparable> newList = new  ArrayList<Comparable>();

         int loopCountA = 0;
         int loopCountB = 0;
         while(true) {
             if(loopCountB == (last - mid)) {
                 while(first + loopCountA <= mid) {
                     newList.add(nums.get(first + loopCountA)); loopCountA++;
                 }
                 break;
             }else if(first + loopCountA > mid) {
                 while(loopCountB < (last - mid)) {
                     newList.add(nums.get(mid + (loopCountB + 1))); loopCountB++;
                 }
                 break;
             }else {
                 if(nums.get(mid + (loopCountB + 1)).compareTo(nums.get(first + loopCountA)) < 0) {
                     newList.add(nums.get(mid + (loopCountB + 1)));
                     loopCountB++;
                 }else {
                     newList.add(nums.get(first + loopCountA));
                     loopCountA++;
                 }
             }
         }       

         for(int i = 0; (i - 1) < (last - first); i++)
             nums.set(first + i, newList.get(i));
    }
}

I would say the problem here (causing the exception) is the way you create sublists - you are using subList(...) method, but it doesn't create another list, just a view for existing one. When you process these different views of one list in different threads you are causing the exception (as every thread changes the same list).

Possible solution would be to change the method to create a sub list or modify constructor to keep a copy, eg:

Merger(List<Comparable> arr) {
    nums = new ArrayList<>(arr);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM