1

I am learning multithreading in Java, and I have a task to copy a file using 2+ threads.

File extension does not matter. So I tried to do it in 2 threads. In the first thread, I copy first half of the file, in the second thread - the second one. I tried to save parts in a byte array, and after go to the main thread and use ByteArrayOutputStream to concatenate these arrays & save a copy of the source file. But it works once-twice in three-six times. I have no idea what's wrong. How to copy a file in multiple threads using Java

public class FirstThread implements Runnable {
private byte[] part1thread;
private RandomAccessFile file;

public FirstThread(RandomAccessFile file, int byteArraySize) {
    this.file = file;
    this.part1thread = new byte[byteArraySize];
}

public void run() {
    try {
        System.out.println("Start the first thread.");

        file.read(part1thread, 0, part1thread.length);

        System.out.println("I am the first thread and I read first part1thread of the file you gave me.");

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

        RandomAccessFile fileOutputWrite = new RandomAccessFile("copy_", "rw");

        System.out.println("First threads results writing...");
        outputStream.write(this.part1thread);
        System.out.println("output 1 thread: " + outputStream);
        fileOutputWrite.write(this.getPart1thread(), 0, this.getPart1thread().length);

        fileOutputWrite.close();
        outputStream.close();

        Main.setCountDownLatch();

    } catch (IOException e) {
        e.printStackTrace();
    }
}

public byte[] getPart1thread() {
    return part1thread;
}

}

public class SecondThread implements Runnable {

private byte[] part2thread;
private RandomAccessFile file;
private long tempBytesSize;

public SecondThread(RandomAccessFile file, int arrayFullSize) {

    this.file = file;

    tempBytesSize = arrayFullSize % 2 == 0 ? arrayFullSize/2 : arrayFullSize/2 + 1;

    System.out.println(arrayFullSize % 2 == 0);

    this.part2thread = new byte[(int)tempBytesSize];

}

public void run() {
    try {
        System.out.println("Start the second thread.");

        file.seek(part2thread.length - 1);
        System.out.println(tempBytesSize);
        file.read(part2thread, 0, (int)tempBytesSize);

        System.out.println("I am the second thread and I read second part2thread of the file you gave me.");


        RandomAccessFile fileOutputWrite = new RandomAccessFile("copy_", "rw");
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

        fileOutputWrite.seek(fileOutputWrite.length() - 1);

        System.out.println("Second threads results writing..");
        outputStream.write(this.getPart2thread());
        System.out.println("output 2 thread: " + outputStream);
        fileOutputWrite.write(this.getPart2thread(), 0, this.getPart2thread().length);

        fileOutputWrite.close();
        outputStream.close();

        Main.setCountDownLatch();

    } catch (IOException e) {
        e.printStackTrace();
    }
}

public byte[] getPart2thread() {
    return part2thread;
}

}

public class Main {

 private static volatile CountDownLatch countDownLatch;

public synchronized static void main(String[] args) throws IOException {

    countDownLatch = new CountDownLatch(2);

    Scanner scanner = new Scanner(System.in);

    System.out.println("Enter file name with extension");
    String fileName = scanner.nextLine();

    RandomAccessFile file = null;
    long partByteSize = 0;
    try {
        file = new RandomAccessFile(fileName, "r");
        partByteSize = file.getChannel().size() / 2;
    } catch (FileNotFoundException e) {
        System.out.println("File not found!");
        System.exit(-1);
    } catch (IOException e) {
        System.out.println("An error has been occured. Cannot work with file.");
        System.exit(-1);
    }

        ExecutorService pool = Executors.newFixedThreadPool(2);

        FirstThread firstThread = new FirstThread(file, (int) partByteSize);
        SecondThread secondThread = new SecondThread(file, (int) file.getChannel().size());

        pool.submit(firstThread);
        pool.submit(secondThread);

        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            System.out.println("Unexpected error.");
            System.exit(-1);
        }

        System.out.println("Return flow control to the main thread");

        ByteArrayOutputStream outputStream;

        file.close();
    System.exit(0);
}

public static void setCountDownLatch() {
    Main.countDownLatch.countDown();
}

}

I tried with *.pdf & *.txt files. *.pdf copies does not open, *.txt files: when I copy only the first or the second half, it works well. But when I tried to copy two parts one by one, it works SOMETIMES. I have no idea how to fix it.

  • 4
    *FYI:* If this is an exercise, that's fine, but be aware that reading a file using multiple threads may actually degrade performance, since the disk will be the performance bottleneck. If you want to use multiple threads, you might consider using one thread for reading the original file, and another thread for writing the copy. You might actually gain performance with that combination, if you're copying to a different disk. – Andreas Jan 21 '19 at 18:32
  • Yes, it is just exercise. I read about multithreading and it's my first steps in multithreading. Degrade performance connected with disk read-write speed, yes? – Irina Voropaeva Jan 21 '19 at 18:36
  • 1
    Degrade performance because reading from multiple locations on the disk in parallel makes the disk arm jump back and forth, and the disk arm moving speed is by far the biggest contributor to performance issues. – Andreas Jan 21 '19 at 18:38
  • 1
    May be the task is simpler than you think (at least 2 threads): Use one thread for reading and one thread for writing the data. – Robert Jan 21 '19 at 20:38

2 Answers2

2

Check this 60-liner (comments inline):

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;

public class SOQ54295781 {

    private static final int BUFF_SIZE = 4096;

    public static void main(final String[] args) throws IOException {
        // create a "piped stream":
        final PipedOutputStream output = new PipedOutputStream();
        final PipedInputStream input = new PipedInputStream(output);
        //"read" thread:
        Thread thread1 = new Thread(() -> {          
            System.out.println("t1 start...");
            //opens source file ...
            try (FileInputStream fileIn = new FileInputStream("test.pdf")) { //args[0]
                // ... and copies to pipe.
                long totalBytes = copy(fileIn, output, "t1", "from src file into pipe");
                System.out.format("Reading from source file completed. Total: %d bytes.%n", totalBytes);
                fileIn.close();
            } catch (IOException e) {
                e.printStackTrace(System.err);
            } finally {
                try {
                    output.close();
                } catch (IOException ex) {
                    ex.printStackTrace(System.err);
                }
            }
            System.out.println("t1 done.");
        });
        //"write" thread:
        Thread thread2 = new Thread(() -> {
            System.out.println("t2 start...");
            // opens dest file ...
            try (FileOutputStream fileOut = new FileOutputStream("test.copy.pdf")) {//args[1]
                // ... and copies from pipe.
                long totalBytes =  copy(input, fileOut, "t2", "from pipe into dest file");
                System.out.format("Writing to dest file completed. Total: %d bytes.%n", totalBytes);
                fileOut.close();
            } catch (IOException e) {
                e.printStackTrace(System.err);
            } finally {
                try {
                    input.close();
                } catch (IOException ex) {
                    ex.printStackTrace(System.err);
                }
            }
            System.out.println("t2 done.");
        });
        thread1.start();
        thread2.start();
    }

    // thx to https://stackoverflow.com/a/22128215/592355 resp. guava extended by some debug output
    private static long copy(InputStream from, OutputStream to, String thread, String msg)
            throws IOException {
        byte[] buf = new byte[BUFF_SIZE];
        long total = 0;
        while (true) {
            int r = from.read(buf);
            if (r == -1) {
                break;
            }
            to.write(buf, 0, r);
            total += r;
            System.out.format("I am %s, and I copied %d bytes %s.%n", thread, r, msg);
        }
        return total;
    }
}

Output:

t2 start...
t1 start...
I am t2, and I copied 1024 bytes from pipe into dest file.
I am t2, and I copied 1024 bytes from pipe into dest file.
I am t2, and I copied 1024 bytes from pipe into dest file.
I am t1, and I copied 4096 bytes from src file into pipe.
I am t2, and I copied 1024 bytes from pipe into dest file.
I am t2, and I copied 1024 bytes from pipe into dest file.
I am t2, and I copied 1024 bytes from pipe into dest file.
I am t2, and I copied 1024 bytes from pipe into dest file.
I am t1, and I copied 4096 bytes from src file into pipe.
I am t2, and I copied 1024 bytes from pipe into dest file.
// ...
I am t2, and I copied 1024 bytes from pipe into dest file.
I am t1, and I copied 782 bytes from src file into pipe.
Reading from source file completed. Total: 6980366 bytes.
t1 done.
I am t2, and I copied 782 bytes from pipe into dest file.
Writing to dest file completed. Total: 6980366 bytes.
t2 done.

see also:

xerx593
  • 12,237
  • 5
  • 33
  • 64
0

I added method

    public boolean getState() {
    return Thread.currentThread().isAlive();
}

into the FirstThread class

I made firstThread & secondThread as members of the Main class.

if (Main.getFirstThread().getState()) {

I added if condition in run() method of SecondThread Else - we recursively try to run method run() again till first thread starts.

And lol I need

resultFile.seek(resultFile.length() - 0);

When I write a copied file, cos without "- 0" it does not work.

  • `cos without "- 0" it does not work.` that's impossible, it can't make a difference to a Java runtime or that would be a broken runtime. – zapl Jan 21 '19 at 21:55