需要线程专家的眼睛在这里...

我正在使用POC应用程序,在该应用程序上将文件上传到FTP服务器上

在FTP服务器中有多个文件夹。根据输入响应,我从该文件夹中读取文件并移至另一个文件夹

该应用程序一次可以访问多个线程。

所以问题是这样的:

假设FTP有一个文件夹Folder_A和A_A_FOLDER
现在Folder_A有10个文件。
出现了一个线程,并从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
}

文件夹名称决定需要处理的文件夹

现在我开始面临性能问题

因为该方法是同步的,所以所有线程都将等待,直到处理线程未完成其任务为止。

我可以通过以下步骤对此进行改进:

(在寻求解决方案之前,这里是一些深入探讨该问题的故事)

正如我提到的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读取,则将其移至与B相同的A_T中(移至B_T)

现在:

假设m1方法有4个线程,文件夹A有3个线程,文件夹B有1个线程
如果某种方法基于fileName参数同步请求以便我改善性能,则意味着1个线程将在A线程上工作,另外2个线程将阻塞,因为fileName与它们相同,因此它们将等到第一个线程未完成时才执行线程4将并行执行的任务无需任何锁定过程即可工作,因为文件名不同

那么,如何在代码级别实现这一目标(与fileName同步)呢?

注意:我知道我可以使用资源的静态锁定列表然后锁定fileName资源来打破此逻辑
例如:
private final Object A = new Object();
private final Object B = new Object();

但是这种方法的问题是可以动态添加文件夹,所以我不能这样做。

需要你们的帮助。

最佳答案

一种方法是保持每个目录的锁定:

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());
    }
}

09-10 06:25
查看更多