java threads access outer class from static inner class
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(m开发者_StackOverflow社区atcher.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) {
}
}
}
}
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.
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.
精彩评论