簡體   English   中英

Java Fork-Join不使用大型ArrayList

[英]Java Fork-Join not working with large ArrayList

我對並行性和並發性都很陌生,我正在嘗試使用Java中的Fork-Join實現中值濾波器算法。 基本上我將輸入文件讀入ArrayList並使用該列表生成過濾中位數的新ArrayList(包括原始ArrayList的第一個和最后一個元素)。

現在我設法制作了算法的串行/順序版本,它工作正常。 但是,當我嘗試制作Fork-Join版本時,它似乎不適用於大型ArrayLists(100000+)。 我嘗試使用一個非常小的大小為5的ArrayList,它工作正常。 我似乎無法找到我的錯誤(我肯定是一個邏輯錯誤和/或實現錯誤)。 任何幫助,將不勝感激。

這是順序算法片段:

    //Add first boundary element to output ArrayList
    outputElements.add(this.elements.get(0));

    //Start Filter Algorithm 
    while(elements.size()-counter >= filterSize){
        for(int i = 0; i<filterSize; i++){
            tempElements.add(this.elements.get(i+counter));
            if(i==filterSize){
                break;
            }
        }

        Collections.sort(tempElements);
        outputElements.add(tempElements.get((filterSize-1)/2));

        counter++;
        tempElements.clear();
    }

    //Add last boundary element to output ArrayList.
    if (elements != null && !elements.isEmpty()) {
        outputElements.add(elements.get(elements.size()-1));
    }//End Filter Algorithm

這是我制作的並行類。 這是不起作用的部分:

public class Parallel extends RecursiveTask<List<Float>>{
    int lo;
    int hi;
    int filterSize;
    String outFile; //Output file name.
    int arraySize;
    List<Float> elements = new ArrayList<Float>();
    List<Float> tempElements = new ArrayList<Float>();
    List<Float> outputElements = new ArrayList<Float>();
    int counter = 0;
    static final int SEQUENTIAL_CUTOFF=1000;

    public Parallel(List<Float> elements, int filterSize, String outFile, int lo, int hi) {
        this.lo = lo;
        this.hi = hi;
        this.elements = elements;
        this.outFile = outFile;
        this.filterSize = filterSize;       
        if(lo == 0){
            outputElements.add(this.elements.get(0));
        }
    }
    @Override
    protected List<Float> compute() {
        long startTime = System.nanoTime(); //Algorithm starts here 
        if((hi-lo) < SEQUENTIAL_CUTOFF) {
            while(hi-counter >= filterSize){
                for(int i = lo; i<filterSize; i++){
                    tempElements.add(this.elements.get(i+counter));
                    if(i==filterSize){
                        break;
                    }
                }               
                Collections.sort(tempElements);
                outputElements.add(tempElements.get((filterSize-1)/2));
                counter++;
                tempElements.clear();
                return outputElements;
            }
          }else{              
              Parallel left = new Parallel(this.elements, this.filterSize, this.outFile, this.lo, ((this.lo + this.hi)/2));
              Parallel right = new Parallel(this.elements, this.filterSize, this.outFile, ((this.hi + this.lo)/2), this.hi);
              left.fork();

              List<Float> leftArr = new ArrayList<Float>();
              List<Float> rightArr = new ArrayList<Float>();

             rightArr =  right.compute();
             leftArr = left.join();

             List<Float> newList = new ArrayList<Float>();
             newList.addAll(leftArr);
             newList.addAll(rightArr);       

          }
        long endTime = System.nanoTime();//Algorithm ends here.

        //Write elements to output file 
        PrintWriter writeOutput = null;
        try {
            writeOutput = new PrintWriter(this.outFile, "UTF-8");
        } catch (FileNotFoundException | UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        writeOutput.println(outputElements.size());//Number of lines
        for(int i=0; i<outputElements.size();i++){
            writeOutput.println(i+1 + " " + outputElements.get(i)); //Each line is written
        }

        writeOutput.close(); //Close when output finished writing.
        System.out.println("Parallel complete");
        return null;
    }
}

任何幫助都非常感謝。 在花了幾個小時研究SO和谷歌后,我無法做到這一點。

編輯:musical_coder建議發布我面臨的錯誤,他們在這里。 這是很多錯誤:

Exception in thread "main" java.lang.IndexOutOfBoundsException
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
    at java.util.concurrent.ForkJoinTask.getThrowableException(ForkJoinTask.java:536)
    at java.util.concurrent.ForkJoinTask.reportResult(ForkJoinTask.java:596)
    at java.util.concurrent.ForkJoinTask.join(ForkJoinTask.java:640)
    at java.util.concurrent.ForkJoinPool.invoke(ForkJoinPool.java:1521)
    at main.main(main.java:45)
Caused by: java.lang.IndexOutOfBoundsException: Index: 1, Size: 0
    at java.util.ArrayList.rangeCheck(ArrayList.java:635)
    at java.util.ArrayList.get(ArrayList.java:411)
    at Parallel.compute(Parallel.java:44)
    at Parallel.compute(Parallel.java:57)
    at Parallel.compute(Parallel.java:57)
    at Parallel.compute(Parallel.java:57)
    at Parallel.compute(Parallel.java:57)
    at Parallel.compute(Parallel.java:57)
    at Parallel.compute(Parallel.java:57)
    at Parallel.compute(Parallel.java:57)
    at Parallel.compute(Parallel.java:57)
    at Parallel.compute(Parallel.java:57)
    at Parallel.compute(Parallel.java:57)
    at Parallel.compute(Parallel.java:57)
    at Parallel.compute(Parallel.java:1)
    at java.util.concurrent.RecursiveTask.exec(RecursiveTask.java:93)
    at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:334)
    at java.util.concurrent.ForkJoinWorkerThread.execTask(ForkJoinWorkerThread.java:604)
    at java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:784)
    at java.util.concurrent.ForkJoinPool.work(ForkJoinPool.java:646)
    at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:398)

通常,您應該避免在多線程代碼中使用ArrayList ,因為它不是線程安全的:

請注意,此實現不同步。 如果多個線程同時訪問ArrayList實例,並且至少有一個線程在結構上修改了列表,則必須在外部進行同步。

我沒有在你發布的片段中看到任何同時修改列表的內容,但我確實看到你將this.elements傳遞給子Parallel實例,這意味着你至少做了一些冒險的事情(共享指向可變,非線程的指針) - 線程之間的安全對象)。

作為第一遍,替換this.elements = elements; 在您的Parallel構造函數中具有以下內容:

this.elements = Collections.unmodifiableList(elements);

通過使列表不可修改 ,您將確保如果您的Parallel代碼試圖改變列表,您將在失敗點處得到明確的錯誤。 這並不妨礙Parallel之外的其他東西修改原始elements列表,但它是一種快速,簡單的方法來驗證Parallel的行為是否正確。 如果得到UnsupportedOperationException ,則需要重新設計Parallel類 - 不能同時修改ArrayList

如果沒有得到UnsupportedOperationException ,則其他內容正在修改您的列表。 您需要找到並刪除它。


一旦你弄清楚導致你的列表同時發生變異的原因,你可以嘗試確定最好的前進方法。 通過所有“正確”的方式在線程之間共享數據超出了我希望在這個答案中涵蓋的范圍,但這里有一些一般的經驗法則:

  • 避免使用可變數據結構 - 將Parallel類設計為僅處理來自不可變數據結構的數據,例如Guava的ImmutableList 默認情況下,不可變數據結構是線程安全的。
  • 使用線程安全的數據結構 - 例如, ConcurrentLinkedQueue是多個進程讀取和寫入同一數據結構的線程安全方式。 ConcurrentHashMap是另一個常用的類。 你需要什么,取決於你想要做什么,但這些都是很好的起點。
  • 最大限度地減少並發操作的范圍 - 即使使用並發數據結構,您的總體目標應該是使每個任務獨立運行,除了從共享源讀取和寫入共享接收器之外。 對僅對一個線程可見的對象執行盡可能多的工作。
  • 同步 - 我注意到Parallel在沒有任何顯式同步的情況下寫入outFile 這很危險,並且可能會引入問題(崩潰或更糟的數據損壞)。 一次只能有一個線程寫入文件。 通過使用專用的文件寫入線程或通過顯式同步文件寫入操作來實現。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM