这里需要专家眼睛......
我在一个poc应用程序,我在FTP服务器上传文件
在FTP服务器中有多个文件夹。基于输入响应,我从文件夹中读取文件并移动到另一个文件夹
应用程序一次可以通过多个线程访问。
所以问题是:
假设FTP有一个文件夹Folder_A和A_A_FOLDER 现在Folder_A有10个文件。 一个线程来自FTP并从FTP读取10个文件并开始计算, 它逐个计算,然后移动到A_A_FOLDER 它在过程的中间(假设它成功地将5个文件从Folder_A移动到A_A_FOLDER) 然后另一个线程来了,它挑选剩余的5个文件,因为它们被线程1处理不足,所以线程2也开始处理这5个文件
这里重复文件问题
void m1(String folderName) {
// FTP related code
}
我已使用已同步关键字
解决了此问题现在每个事情都在同步,所有处理工作正常
synchronized void m1(String folderName) {
// code
}
folderName 决定需要处理哪个文件夹
现在我开始面临性能问题
因为该方法是同步的,所以所有线程都要等到处理线程没有完成它的任务。
我可以通过以下步骤来改进:
(在这里找到一个解决方案之前就是要解决这个问题的一些故事)
正如我所提到的m1方法的folderName参数决定了哪个文件夹将处理, 假设我在Ftp服务器中有4个文件夹(A,B,A_T,B_T),2个文件夹是需要从中读取数据的文件夹(A和B), 2个文件夹是数据将移动的文件夹(A_T和B_T)
这里不关心A_T和B_T,因为它们对于每个文件夹A和B都是唯一的 因此,如果该方法将从A读取,那么它将其移动到A_T,对于B(移至B_T)
现在:
假设有4个线程来到m1方法,3个线程用于文件夹A,1个用于文件夹B. 如果以某种方式同步方法同步请求基于fileName参数,所以我可以提高性能,意味着1个线程将工作另一个2线程将阻止因为fileName是相同的,所以他们将等到第一个线程没有完成它任务线程4将并行没有任何锁定过程的工作,因为它的文件名不同
那么如何在代码级别实现这一点(在fileName上同步)?
注意:我知道我可以使用资源的静态锁定列表然后锁定fileName资源来打破这个逻辑 e.g:
private final Object A = new Object();
private final Object B = new Object();
但这种方法的问题是文件夹可以动态添加,所以我不能这样做。
需要你帮助的人。
答案 0 :(得分:3)
一种方法是为每个目录维护一个锁:
public class DirectoryTaskManager {
public static void main(String[] args) throws IOException {
DirectoryTaskManager manager = new DirectoryTaskManager();
manager.withDirLock(new File("Folder_A"), () -> System.out.println("Doing something..."));
}
public void withDirLock(File dir, Runnable task) throws IOException {
ReentrantLock lock = getDirLock(dir);
lock.lock();
try {
task.run();
} finally {
lock.unlock();
}
}
private Map<File, ReentrantLock> dirLocks = Collections.synchronizedMap(new HashMap<>());
public ReentrantLock getDirLock(File dir) throws IOException {
// Resolve the canonical file here so that different paths
// to the same file use the same lock
File canonicalDir = dir.getCanonicalFile();
if (!canonicalDir.exists() || !canonicalDir.isDirectory()) {
throw new FileNotFoundException(canonicalDir.getName());
}
return dirLocks.computeIfAbsent(canonicalDir, d -> new ReentrantLock());
}
}
答案 1 :(得分:1)
感谢@teppic和@OlegSklyar的指导 最后这里是完整的工作示例,
FolderImpl - &gt;有方法名称调用,可以由许多线程访问
我使用过ConcurrentHashMap(使用锁完成写入时读取速度非常快。)比synchronizedMap快一点 它将保存文件夹名称和ReentrantLock,因此锁定将对文件夹名称
起作用public class FolderImpl {
private FolderImpl(){
System.out.println("init................");
}
private ConcurrentHashMap<String, ReentrantLock> concurrentHashMap= new ConcurrentHashMap();
private static final FolderImpl singleTon = new FolderImpl();
public static FolderImpl getSingleTon() {
return singleTon;
}
public void call(String name) throws Exception{
ReentrantLock getDirLock = getDirLock(name);
getDirLock.lock();
try {
for (int i = 0; i < 100; i ++) {
System.out.println(i+":"+name+":"+Thread.currentThread().getName());
try {
Thread.sleep(30);
} catch (Exception e) {
e.printStackTrace();
}
}}finally {
getDirLock.unlock();
}
}
public ReentrantLock getDirLock(String site) {
return concurrentHashMap.computeIfAbsent(site, d -> new ReentrantLock());
}
}
public class TaskCaller extends Thread{
public FolderImpl singleTon = FolderImpl.getSingleTon();
public TaskCaller(String name) {
super();
this.name = name;
}
private String name;
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("Name:"+Thread.currentThread().getName());
try {
singleTon.call(name);
sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
TestExecution类将执行10个线程进行测试
public class TestExecution {
public static void main(String[] args) {
TaskCaller testThreadCC = new TaskCaller("A_FOLDER");
TaskCaller testThreadCC2 = new TaskCaller("A_FOLDER");
TaskCaller testThreadCC3 = new TaskCaller("B_FOLDER");
TaskCaller testThreadCC4 = new TaskCaller("C_FOLDER");
TaskCaller testThreadCC5 = new TaskCaller("C_FOLDER");
TaskCaller testThreadCC6 = new TaskCaller("C_FOLDER");
TaskCaller testThreadCC7 = new TaskCaller("A_FOLDER");
TaskCaller testThreadCC8 = new TaskCaller("A_FOLDER");
TaskCaller testThreadCC9 = new TaskCaller("B_FOLDER");
TaskCaller testThreadCC10 = new TaskCaller("B_FOLDER");
testThreadCC.start();
testThreadCC2.start();
testThreadCC3.start();
testThreadCC4.start();
testThreadCC5.start();
testThreadCC6.start();
testThreadCC7.start();
testThreadCC8.start();
testThreadCC9.start();
testThreadCC10.start();
}
}