如何将文件作为输入并在多个线程中工作?

时间:2017-02-03 11:45:19

标签: java multithreading io

我有这段代码来了解如何从URL获取状态代码:

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * @author Crunchify.com
 * 
 */

 class j {
    public static void main(String args[]) throws Exception {

        String[] hostList = { "http://example.com", "http://example2.com","http://example3.com" };

        for (int i = 0; i < hostList.length; i++) {

            String url = hostList[i];
            String status = getStatus(url);

            System.out.println(url + "\t\tStatus:" + status);
        }
    }

    public static String getStatus(String url) throws IOException {

        String result = "";
        try {
            URL siteURL = new URL(url);
            HttpURLConnection connection = (HttpURLConnection) siteURL
                    .openConnection();
            connection.setRequestMethod("HEAD");
            connection.connect();

            int code = connection.getResponseCode();

                result = Integer.toString(code);

        } catch (Exception e) {
            result = "->Red<-";
        }
        return result;
    }
}

我检查了它的小输入它工作正常。但我有数百万个域需要扫描。我有一个包含它的文件。

  1. 我想知道如何将文件作为此代码的输入。
  2. 我希望代码在多线程中工作。说线程数应该超过20000,这样我的输出会更快。
  3. 如何将其写入另一个文件?
  4. 请帮助我。如果可能的话,我想知道哪个Bandwidth Savvy方法可以做同样的工作。我想让代码更快。我怎么能用我的代码做这些事情? Java版本:

    java version "1.8.0_121"
    Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
    Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)
    

4 个答案:

答案 0 :(得分:1)

跨线程共享文件时会遇到问题。读取文件然后生成一个线程来处理文件中的每个记录要好得多。

创建一个线程是非常简单的资源,因此线程池很有用,因此可以重用线程。

您是否希望所有线程都写入单个文件?

我会使用线程和编写器之间的共享列表来做到这一点。其他人可能有更好的主意。

如何做到这一切取决于Java版本。

答案 1 :(得分:1)

您可以使用ExecutorService并设置要使用的话题编号 ExecutorService实例将处理线程管理。
您只需要为其提供执行和调用所有任务执行的任务。

执行完所有任务后,您可以获得结果 在call()实施的Callable方法中,我们返回带有分隔符的String,以指示请求的网址和响应代码。
例如:http://example3.com||301http://example.com||200等......

我没有编写代码来读取文件并在其他文件中存储任务的结果。你不应该很难实现它。

这是主要课程:

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Main {

    public static void main(String[] args) throws InterruptedException {

        String[] hostList = { "http://example.com", "http://example2.com", "http://example3.com" };

        int nbThreadToUse = Runtime.getRuntime().availableProcessors() - 1;
        ExecutorService executorService = Executors.newFixedThreadPool(nbThreadToUse);
        Set<Callable<String>> callables = new HashSet<Callable<String>>();
        for (String host : hostList) {
            callables.add(new UrlCall(host));
        }

        List<Future<String>> futures = executorService.invokeAll(callables);

        for (Future<String> future : futures) {
            try {
                String result = future.get();
                String[] keyValueToken = result.split("\\|\\|");
                String url = keyValueToken[0];
                String response = keyValueToken[1];
                System.out.println("url=" + url + ", response=" + response);

            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }

        executorService.shutdown();
    }

}

这是UrlCall,用于执行对url的调用的Callable实现。 UrlCall在其构造函数中接受要测试的URL。

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.Callable;

public class UrlCall implements Callable<String> {

    private String url;

    public UrlCall(String url) {
        this.url = url;
    }

    @Override
    public String call() throws Exception {
        return getStatus(url);
    }

    private String getStatus(String url) throws IOException {

        try {
            URL siteURL = new URL(url);
            HttpURLConnection connection = (HttpURLConnection) siteURL.openConnection();
            connection.setRequestMethod("HEAD");
            connection.connect();

            int code = connection.getResponseCode();
            return url + "||" + code;

        } catch (Exception e) {
            //FIXME to log of course
            return url + "||exception";
        }

    }

}

答案 2 :(得分:1)

这样做你想要的:

输入列表文件(c://lines.txt)

http://www.adam-bien.com/
http://stackoverflow.com/
http://www.dfgdfgdfgdfgdfgertwsgdfhdfhsru.de
http://www.google.de

线程:

import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.Callable;

public class StatusThread implements Callable<String> {

    String url;

    public StatusThread(String url) {
        this.url = url;
    }

    @Override
    public String call() throws Exception {

        String result = "";
        try {
            URL siteURL = new URL(url);
            HttpURLConnection connection = (HttpURLConnection) siteURL.openConnection();
            connection.setRequestMethod("HEAD");
            connection.connect();

            int code = connection.getResponseCode();

            result = Integer.toString(code);

        } catch (Exception e) {
            result = "->Red<-";
        }
        return url + "|" + result;
    }
}

主程序:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Stream;

public class CallableExample {
    public static void main(String[] args) throws IOException {

        // Number of threads
        int numberOfThreads = 10;

        // Input file
        String sourceFileName = "c://lines.txt"; // Replace by your own
        String targetFileName = "c://output.txt"; // Replace by your own

        // Read input file into List    
        ArrayList<String> urls = new ArrayList<>();
        try (Stream<String> stream = Files.lines(Paths.get(sourceFileName ))) {
            stream.forEach((string) -> {
                urls.add(string);
            });

        } catch (IOException e) {
            e.printStackTrace();
        }

        // Create thread pool
        ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(numberOfThreads);
        List<Future<String>> resultList = new ArrayList<>();

        // Launch threads
        for(String url : urls) {
            StatusThread statusGetter = new StatusThread(url);
            Future<String> result = executor.submit(statusGetter);
            resultList.add(result);
        }

        // Use results
        FileWriter writer;
        writer = new FileWriter(targetFileName);
        for (Future<String> future : resultList) {
            try {
                String oneResult = future.get().split("\\|")[0] + " -> " + future.get().split("\\|")[1];

                // Print the results to the console
                System.out.println(oneResult);

                // Write the result to a file
                writer.write(oneResult + System.lineSeparator());

            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }
        writer.close();


        // Shut down the executor service
        executor.shutdown();
    }
}

不要忘记:

  • 创建输入文件并指向它(c://lines.txt)
  • 更改线程数以获得最佳结果

答案 3 :(得分:1)

我同意此处公开的线程池方法。 多线程包括利用其他线程花费等待的时间(我想在他的情况下:远程站点响应)。它不会增加处理能力。然后大约10个线程似乎合理(更多取决于硬件)。

在我读到的答案中,似乎忽略了一个重要的观点,即OP谈论数百万的域名。然后我会阻止将整个文件加载到内存中的迭代列表中。我宁愿在一个循环(文件读取)中合并所有内容,而不是3(读取,ping,写入)。

outputWriter

<img src="/resize/45-800/files/myImage.jpeg" alt="Chania"> 将是一个带有 synchronized 方法的类型,可以写入输出流。