首页 > 解决方案 > 具有多线程的调度多任务处理,没有线程重叠

问题描述

我有 5 个参数化线程指向 5 个文件夹,其中参数是文件夹的名称。假设文件夹名称是 A、B、C、D 和 E。

每个文件夹将有多个文件,需要对其执行某些操作。

对每个文件夹下的所有文件执行的操作将相同,即任务将相同。这整个事情需要不断地运行,即它必须在一个时间表中。

我尝试过的事情:

  1. 使用多线程调度(即 5 个参数化线程处理同一个任务)-> 但这会导致线程重叠,因为任务很常见并且不会生成所需的输出。

  2. 使用多任务调度,即为每个实现 Runnable 的文件夹创建一个单独的类,并为每个类使用 executor.scheduleAtFixedRate。这将导致同步操作,这意味着直到第一个文件夹的操作没有结束,其他 4 个文件夹的处理将不会开始。我们不能在 newScheduledThreadPool 中增加 corePoolSize,因为它会产生与第 1 点相同的重叠问题。

因此,我正在寻求帮助来解决我的线程在底层任务上不重叠的问题。

以图形式总结上述问题

场景 2 的虚拟代码:

class FolderA implements Runnable{
    private final String fileName;

    FolderA(String fileName){
        this.fileName=fileName;
    }
    @Override
    public void run() {
        ScheduleJob.insideRun(fileName);
    }
}

class FolderB implements Runnable{...}
class FolderC implements Runnable{...}
class FolderD implements Runnable{...}
class FolderE implements Runnable{...}

public class ScheduleJob{
    ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
    
    FolderA folderA = new FolderA("folderA");
    executor.scheduleAtFixedRate(folderA, 60,60, TimeUnit.SECONDS);
    
    FolderB folderB = new FolderB("folderB");
    executor.scheduleAtFixedRate(folderB, 60,60, TimeUnit.SECONDS);
    
    --and same for Folder  C D and E
    
     public static void insideRun(String folderName){
     
     //Contains call to various operations that need to 
     be perfomed on each file present in Folder A B C D and E
     
     Operation order : Read, Process, Write for each file
     
     }
    
    
}

标签: javamultithreadingscheduled-tasksschedulermultitasking

解决方案


您的问题不清楚,但我猜您希望处理一个文件夹中的文件不会妨碍或阻止其他文件夹中文件的处理。

多个ExecutorService对象

创建多个ExecutorService对象。每个执行器服务都集中在一个文件夹上。如果您有五个文件夹,则有五个执行器服务,每个文件夹一个执行器服务。

如果您希望每个文件夹一次只处理一个文件,请使每个执行程序服务成为单线程。

将您的任务(您的RunnableCallable)定义为采用指示要处理哪个文件夹的参数。

public void FileProcessor implements Runnable
{
    // Constructor
    public FileProcessor( Path path ) { … } 

    // Implement `Runnable`
    @Override
    public void run() { … }
} 

定义您的文件夹。

List< Path > folders = List.of( pathToA, pathToB, … ) ;

将这些提供给执行器服务的构造函数。

List< ExecutorService > executorServices = new ArrayList<>() ;
for( Path folder : folders )
{
    ExecutorService es = Executors. newSingleThreadExecutor() ;
    executorServices.add( es ) ;
    es.submit( new FileProcessor( folder ) ) ;
}

然后使用该集合executorServices优雅地关闭所有执行器服务。

对于预定的执行器服务,同样的想法。将类型从 更改ExecutorServiceScheduledExecutorService。打电话Executors.newSingleThreadScheduledExecutor。将方法更改submit为计划方法之一。

请注意,即使此答案中提出的解决方案也不能保证所有文件夹都将得到同等的处理。每个执行器服务将由其自己的线程支持。何时安排线程在 CPU 内核上执行以及执行多长时间取决于主机操作系统和 JVM 的心血来潮。

作为 Java 程序员,我们无法直接控制每个线程执行的工作量或顺序。在运行时,文件夹 B 完全有可能处理十几个文件,而文件夹 A 只完成三个文件,而文件夹 C 没有完成。一般来说,随着时间的推移,您会看到工作的平均值,但短期内可能会有所不同。


推荐阅读