2

This builds up on my previous question.

My ftp server has 10 files, say test1.txt, test2.txt and so on. I want to be able to download multiple files (max 3) at the same time. I am calling downloadFilesByPattern(....) If I dont use synchronized on downloadFile() then only some of the file are downloaded not all. If I use synchronized, then all the files are downloaded, but I don't think they are happening in parallel. Is the issue because an instance varible is passed to all threads and a method on that instance is called by all threads.

public class FTPClientService implements IClient {

private String username;
private String password;
private String port;
private String host;
private String path;
FTPClient client = new FTPClient();

private static class DownloadTask implements Runnable {

    private String name;
    private String toPath;
    private IClient client;

    public DownloadTask(String name, String toPath, IClient client) {
        this.name = name;
        this.toPath = toPath;
        this.client = client;
    }

    @Override
    public void run() {
        System.out.println("download = " + name);
        client.downloadFile(name, toPath);
    }
}

public void downloadFilesByPattern(String fileNamePattern, final String outputFilePath) {

    if(!changeDirectory()){
        return;
    }

    try {
        //get a list of file names that match the pattern
        String[] names = client.listNames();

        ExecutorService pool = Executors.newFixedThreadPool(3);
        for (String name : names) {
            //check if the filename matches the pattern
            Pattern pattern =  Pattern.compile(fileNamePattern);
            Matcher matcher = pattern.matcher(name);
            if(matcher.find()){
                System.out.println("Match found = " + name);
                pool.submit(new DownloadTask(name, outputFilePath, this));
            }else{
                System.out.println("No match = " + name);
            }
        }
        pool.shutdown();
        try {
            pool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
        } catch (InterruptedException ex) {
        }

    } catch (IOException ex) {
    }
}

public synchronized void downloadFile(String fileName, String outputFilePath) {

    FileOutputStream fos = null;
    try {
        fos = new FileOutputStream(outputFilePath+"/"+fileName);
        if(this.isFilePresent(fileName)){
            //look for fileName in the path and write it to 
            client.retrieveFile(fileName, fos);
            System.out.println("Downloading file " + fileName + " ...");
        }else{
            System.out.println("Could not find file " + fileName);
        }
    } catch (IOException ex) {
    } finally {
        try {
            fos.close();
        } catch (IOException ex) {
        }
    }
}
}
Community
  • 1
  • 1
user373201
  • 10,945
  • 34
  • 112
  • 168

2 Answers2

4

It's because they all use the same instance of

FTPClient client

You need to either create new instance of FTPClientService for every download/thread or have an instance of FTPClient for every thread. I personally prefer the second variant which can be easily implemented using ThreadLocal.

Andrei LED
  • 2,560
  • 17
  • 21
0

FTPClient is probably not thread safe (what product is it coming from anyway?). You may want to create it right before download or create a pool of FTP clients if you need to reuse it.

Also, I recommend you modify your naming convention a bit as it's very hard to distinguish in the code ftpclient vs your own client.

Alex Gitelman
  • 24,429
  • 7
  • 52
  • 49