0

Recently I am making an exercise using Java 7 FORK/JOIN framework and FileChannel to copy a file. Here is my code (Test.java):

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Test {
    private ArrayList<FileProcessor> processors = new ArrayList<FileProcessor>();

    public Test(){
        String outputDir = "C:\\temp";
        if (!Files.isDirectory(Paths.get(outputDir))) {
           System.out.println("this is not a path");
        } else {
            try {
                //start copying file
                ForkJoinPool pool = new ForkJoinPool();
                int numberOfThread = 2;
                File file = new File("C:\\abc.cdm"); 

                long length = file.length();
                long lengthPerCopy = (long)(length/numberOfThread);
                long position = 0L;

                for (int i = 0; i < numberOfThread; i++) {
                    FileProcessor processor = null;
                    if (i == numberOfThread - 1) {
                        //the last thread
                        processor = new FileProcessor("abc.cdm", "C:\\abc.cdm", "C:\\temp", position, length - position);
                    } else {
                        processor = new FileProcessor("abc.cdm", "C:\\abc.cdm", "C:\\temp", position, lengthPerCopy);
                        position = position + lengthPerCopy + 1;
                    }
                    processors.add(processor);
                    pool.execute(processor);
                }

                do {
                   System.out.printf("******************************************\n");
                   System.out.printf("Main: Parallelism: %d\n", pool.getParallelism());
                   System.out.printf("Main: Active Threads: %d\n", pool.getActiveThreadCount());
                   System.out.printf("Main: Task Count: %d\n", pool.getQueuedTaskCount());
                   System.out.printf("Main: Steal Count: %d\n", pool.getStealCount());
                   System.out.printf("******************************************\n");
                   try
                   {
                      TimeUnit.SECONDS.sleep(1);
                   } catch (InterruptedException e)
                   {
                      e.printStackTrace();
                   }
                } while (!isDone()); //when all the thread not been done

            pool.shutdown();
            System.out.println("copy done");
            } catch (Exception ex) {
                //out an error here...
            }
        }

    }

    private boolean isDone(){
        boolean res = false;
        for (int i = 0; i < processors.size(); i++) {
            res = res || processors.get(i).isDone();
        }
        return res;
    }

    public static void main(String args[]) {
        Test test = new Test();
    }

    class FileProcessor extends RecursiveTask<Integer>
    {
       private static final long serialVersionUID = 1L;
       private long copyPosition;
       private long copyCount;
       FileChannel source = null;
       FileChannel destination = null;
       //Implement the constructor of the class to initialize its attributes
       public FileProcessor(String fileName, String filePath, String outputPath, long position, long count) throws FileNotFoundException, IOException{
           this.copyPosition = position;
           this.copyCount = count;
           this.source = new FileInputStream(new File(filePath)).getChannel().position(copyPosition);
           this.destination = new FileOutputStream(new File(outputPath + "/" + fileName), true).getChannel().position(copyPosition);
       }

       @Override
       protected Integer compute()
       {
           try {
               this.copyFile();
           } catch (IOException ex) {
               Logger.getLogger(FileProcessor.class.getName()).log(Level.SEVERE, null, ex);
           }
           return new Integer(0);
       }

       private void copyFile() throws IOException {

            try {
                destination.transferFrom(source, copyPosition, copyCount);
            } 
            finally {
                if (source != null) {
                    source.close();
                }
                if (destination != null) {
                    destination.close();
                }
            }
        }
    }

}

I run my code, if number of threads is 1, the file is copied exactly, but when number of theads is 2, file "C:\abc.cdm" is 77KB (78335), but after copied, file "C:\temp\abc.cdm" is just (39KB).

Where did I get wrong, please tell me??

Update: My problem has been solves The problem is in isDone method, it must be:

boolean res = true;
for (int i = 0; i < processors.size(); i++) {
    res = res && processors.get(i).isDone();
}
return res;

Also edit the following lines of codes:

File file = new File(selectedFile[i].getPath());
long length = file.length();
new RandomAccessFile("C:\\temp\abc.cdm", "rw").setLength(length);

This is just a practice for FORK/JOIN usage!

jww
  • 97,681
  • 90
  • 411
  • 885
user2758327
  • 41
  • 1
  • 1
  • 4
  • The code you posted won't compile - you have bracket-issues... – Nir Alfasi Dec 25 '14 at 04:50
  • Hello alfasin, I have editted code! – user2758327 Dec 25 '14 at 05:50
  • I think this is not the root cause of your problem, still analyzing, but I doubt the isDone() method. It returns `true` if at least one of the `FileProcessor`s is done. But I think its intention is that it returns `true` if all of the `FileProcessor`s are done. It should be `&&`, not `||`, or simply this: `private boolean isDone() { for (final FileProcessor p : processors) if (!p.isDone()) return false; return true; }`. – Christian Hujer Dec 25 '14 at 06:45
  • I think this also is not the root cause of your problem, but I doubt on the `+ 1` in `position = position + lengthPerCopy + 1`. – Christian Hujer Dec 25 '14 at 06:57
  • For your `printf`-debugging / logging, you might want to use `System.err` instead of `System.out`. `System.out` is not for diagnostic messages, that's what `System.err` is for. – Christian Hujer Dec 25 '14 at 06:59
  • In `printf` you might want to use `%n` instead of `\n`. While `\n` is always `LF`, `%n` is whatever the platform needs, which would be `LF` on UNIX / Mac OS X, `CR` on old Mac OS, `CRLF` on DOS / Windows and maybe even something else on an EBCDIC machine. – Christian Hujer Dec 25 '14 at 07:00
  • In the `finally {}` block which closes `source` and `destination`, you may want to remove the `null`-checks. `source` and `destination` can never be null. You might also want to declare them blank `final`. And close them in the reverse order. Besides, be aware that `source` stays open in your code when `destination` couldn't be opened. In the constructor, you might want to `catch (IOException e)` around `new FileOutputStream() / destination` and if it happened, `source.close(); throw e;` to close `source` and rethrow the exception. – Christian Hujer Dec 25 '14 at 07:03
  • When creating the `FileOutputStream`, you might want to go for `new File(outputPath, fileName)` instead of using `+ "/" +` because the code looks more portable and expresses better what your intention is - deal with a file named `fileName` in the directory `outputPath`, not concatenating a new fileName. – Christian Hujer Dec 25 '14 at 07:08
  • 1
    I assume you are aware this won't make your hard drive spin faster and this solution will be much slower, not just more complicated. – Peter Lawrey Dec 25 '14 at 07:57

1 Answers1

2

Your isDone() method was indeed wrong and you corrected it in the original question. But there is another issue in the FileProcessor. You assume that setting the position on the destination past the end of the file will automatically grow the file when you transfer to it. This is not the case.

Your first segment will always write because the write position is 0 and the file's length cannot be less than zero. That was the 39K you saw, which is roughly half of the total file size. The second segment never got written.

In order to get your code to run, you can do the following at the start:

 File file = new File("C:\\abc.cdm"); 
 long length = file.length();

 new RandomAccessFile("C:\\temp\\abc.cdm", "rw").setLength(length);`