繁体   English   中英

如何在for循环内创建多个线程

[英]How to create multiple threads inside of a for loop

嗨,我对 Java 有点陌生。

我有一种方法可以接收 map,并且对于 map 中的每个键值对,它都会写入文件。

我想在 map 中为每个键值对运行一个线程,以便我可以同时创建多个文件。 不确定执行此操作的正确方法或如何使用执行程序服务来完成此操作。

这是我正在尝试的一个非常简单的示例。 而不是编写用于编写文件的所有代码,我只是在示例中使用 system.out.println :

public class CityWriter
{

 public static void main(String []args)
 {
    LinkedHashMap<Integer, ArrayList<City>> stateNumCitiesMap = new LinkedHashMap<Integer, ArrayList<City>>();

    stateNumCitiesMap  = retrieveStateCitiesMap();

    int numOfThreadsToExecuteAtATime = 10;
    
    ExecutorService executor = Executors.newFixedThreadPool(numOfThreadsToExecuteAtATime);
    
    for(Integer key : stateNumCitiesMap.keySet()) //Could have up to 50 key,values in map
    {   
        executor.execute(writeCitiesOfStateToFile(key, StateNumCitiesMap.get(key)));
    }
    
    executor.shutdown();

 }

public LinkedHashMap<Integer, ArrayList<Cities>> writeCitiesOfStateToFile(int stateNum, List<City> citiesList) 
{
    for(City city : citiesList)
    {
        System.out.println(stateNum +" "+ city);
    }
}

}//end of class

我的问题是它似乎不是在这里并行执行线程。 此外,即使 for 循环将调用执行程序 50 次,我也不想一次运行超过 10 个线程。

请让我知道最有效的方法是什么。

实际上,如果我很好地理解了您的问题,那么您的代码正是您想要的(当然,如果我们在您的代码片段中省略了所有语法错误):

  • 它不会产生超过 10 个线程,因为您在此处指定了Executors.newFixedThreadPool(10)您想要多少个线程
  • 您的所有 x map 条目将作为潜在工作分配给执行者。 然后 executor 将与所有 10 个线程并行运行它们中的每一个(但一次不超过 10 个作业)

您可以尝试使用此代码段并检查是否有多个线程并行执行该工作:

    public static void main(String[] args) {
        Map<Integer, List<String>> stateNumCitiesMap = new LinkedHashMap<>();

        for (int i = 0; i < 100; i++) {
            stateNumCitiesMap.put(i, Collections.singletonList("ABC"));
        }

        ExecutorService executor = Executors.newFixedThreadPool(10);

        for (Integer key : stateNumCitiesMap.keySet()) {
            executor.execute(() -> writeCitiesOfStateToFile(key, stateNumCitiesMap.get(key)));
        }

        executor.shutdown();
    }

    public static void writeCitiesOfStateToFile(int stateNum, List<String> citiesList) {
        for (String city : citiesList) {
            System.out.println(stateNum + " " + Thread.currentThread().getName());
        }
    }

如果您不想将作业一个一个地交给执行者,您可以一次传递一批。

 public static void main(String[] args) throws InterruptedException {
        Map<Integer, List<String>> stateNumCitiesMap = new LinkedHashMap<>();

        for (int i = 0; i < 100; i++) {
            stateNumCitiesMap.put(i, Collections.singletonList("ABC"));
        }

        ExecutorService executor = Executors.newFixedThreadPool(10);

        List<Callable<Void>> jobs = new ArrayList<>();
        for (Integer key : stateNumCitiesMap.keySet()) {
            jobs.add(() -> {
                writeCitiesOfStateToFile(key, stateNumCitiesMap.get(key));
                return null;
            });
        }
        executor.invokeAll(jobs);

        executor.shutdown();
    }

    public static void writeCitiesOfStateToFile(int stateNum, List<String> citiesList) {
        for (String city : citiesList) {
            System.out.println(stateNum + " " + Thread.currentThread().getName());
        }
    }

在 java 中,您需要为您希望在线程中运行的任何 object 提供可运行接口,您没有这样做,而这正是执行程序所期望的。

 executor.execute(() -> your function )

实际上是

 executor.execute(new Runnable() {
                @Override
                public void run() {
                    // your code 
                }
            });

该方法没有实现runnables,只有在runnable的run方法中才会被线程化

原因是 executor 使用了一种观察者模式,并且你订阅了 runnable 到它,executor 然后运行 run 方法

来自 java 文档:

Runnable 接口应由其实例旨在由线程执行的任何 class 实现。 class 必须定义一个没有 arguments 的方法,称为 run。 该接口旨在为希望在活动时执行代码的对象提供通用协议。 例如,Runnable 由 class Thread 实现。 处于活动状态仅意味着线程已启动且尚未停止。

也可以让方法本身返回一个runnable

 public static Runnable writeCitiesOfStateToFile(params) {

        return  () -> System.out.println(params);
    }

您可以使用“invokeAll”方法进行多次执行,甚至获得它们的结果(无论是否完成)。 即使他们是 50,它也会为他们使用 10 个线程。当所有任务完成时,将返回结果。 像下面这样的东西,把它当作伪。

Callable<int> callableTask = (fileName) -> {
// implement write to the file
        return 0;
    };
ExecutorService executor = Executors.newFixedThreadPool(10);
     
List<Callable<int>> tasksList;
for(City city : citiesList)
{
    tasksList.add(callableTask(city.toString()));
}

executor.invokeAll(tasksList);

Executor#execute可能是同步的

你说:

它似乎不是在这里并行执行线程

你没有解释这种看法的原因。

但是,仅供参考,情况可能确实如此。 您在ExecutorService上调用了execute方法。

    for(Integer key : stateNumCitiesMap.keySet()) //Could have up to 50 key,values in map
    {   
        executor.execute(writeCitiesOfStateToFile(key, StateNumCitiesMap.get(key)));
    }

execute方法继承自Executor接口,即ExecutorService的超接口。 该接口及其方法被记录为可能异步运行您的任务。 引用 Javadoc:

根据 Executor 实现的判断,该命令可以在新线程、池线程或调用线程中执行。

所以你可能确实看到了顺序的非线程同步执行而不是异步。

根据我对ExecutorService方法submitinvokeAllinvokeAny的阅读,这些方法似乎有望始终异步运行。

不过,考虑到您选择ExecutorService实现,我不相信这种同步行为正在发生。 您对Executors.newFixedThreadPool的调用会生成ThreadPoolExecutor类型的 object 。 简要查看该具体类的execute方法的源代码,它似乎总是异步工作(尽管我不完全确定)。

尽管如此,我们似乎应该总是在使用Executor#execute时假设异步执行。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM