簡體   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