Java 在方法的ArrayList循环中使用线程

Java 在方法的ArrayList循环中使用线程,java,multithreading,arraylist,Java,Multithreading,Arraylist,我有一个ArrayList[big one read from a file],我想通过多线程读取它的内容,并处理每个字符串,反复调用一个方法并将其打印到一个文件中。我已经给出了代码的工作结构。。然而,如果我不陷入与线程同步相关的异常中,我就无法编写我想要的代码。。。 我不熟悉线程的概念。。我想找到一种有效的方法来解决这个问题。我已经研究了其他与线程和ArrayList相关的解决方案,但还没有找到适合我的解决方案。。任何关于如何进行的建议都将不胜感激 import java.io.Buffer

我有一个ArrayList[big one read from a file],我想通过多线程读取它的内容,并处理每个字符串,反复调用一个方法并将其打印到一个文件中。我已经给出了代码的工作结构。。然而,如果我不陷入与线程同步相关的异常中,我就无法编写我想要的代码。。。 我不熟悉线程的概念。。我想找到一种有效的方法来解决这个问题。我已经研究了其他与线程和ArrayList相关的解决方案,但还没有找到适合我的解决方案。。任何关于如何进行的建议都将不胜感激

 import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
public class threadingWithMathod {
    public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException {
        ArrayList<String> samples=readurls("path/to/sample.csv");
        PrintStream filewriter = new PrintStream(new File("path/to/result.csv"), "UTF-8");
        for (int i = 0; i < samples.size(); i++) {
            String string1 = samples.get(i);
            String string2 = samples.get(i+1);
            ///Need Info As to how process with Threading without clashing
            /// sampleProcessString need to be called repeatedly
            //sampleProcessString(filewriter,string) by 2-3 threads
        }
    }
    
    public static void sampleProcessString(PrintStream filewriter,String string) {
        filewriter.println(processedString(string));
    }
    private static Object processedString(String string) {
        //Intended to generate a new line by using a Sql query
        //This method will be using a connection to a mysql data base based on sample
        return string+"++> done something";
    }
    public static ArrayList<String> readurls(String filename) {
        ArrayList<String> aslink=new ArrayList<String>();
        BufferedReader reader;
        try {
            reader = new BufferedReader(new FileReader( filename));
            String line = reader.readLine();
            while (line != null) {
                    aslink.add(line);   
                line = reader.readLine();
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return aslink;
    }

}
导入java.io.BufferedReader;
导入java.io.File;
导入java.io.FileNotFoundException;
导入java.io.FileReader;
导入java.io.IOException;
导入java.io.PrintStream;
导入java.io.UnsupportedEncodingException;
导入java.util.ArrayList;
公共类threadingWithMathod{
公共静态void main(字符串[]args)引发FileNotFoundException、UnsupportedEncodingException{
ArrayList samples=readurls(“path/to/sample.csv”);
PrintStream filewriter=新的PrintStream(新文件(“path/to/result.csv”),“UTF-8”);
对于(int i=0;i完成某件事”;
}
公共静态ArrayList readurls(字符串文件名){
ArrayList aslink=新的ArrayList();
缓冲读取器;
试一试{
reader=newbufferedreader(newfilereader(filename));
字符串行=reader.readLine();
while(行!=null){
aslink.add(行);
line=reader.readLine();
}
reader.close();
}捕获(IOE异常){
e、 printStackTrace();
}
返回aslink;
}
}

由于物理磁盘访问,按顺序读取大文件的速度最快。 可以使用内存映射字节缓冲区。 在您的情况下(每行处理)(默认UTF-8)可能就足够了

这是细粒度并发。处理可以通过使用线程池ExecutorService并行完成,因为将有许多线程

你会把结果弄乱的。如果出现问题,请在
文件.lines
lambda中传递行号

对于收集结果、在内存中查询结果并将其异步写入文件,可以查看是否有高性能记录器。可能必须这样做 重新实现其功能(取消日志格式)。因此,一个队列线程和一个用于写入文件的线程(一个大字节缓冲区)

可以考虑压缩输出(.CSV.Gz);这将是进一步网络传输的空间/时间增益


实现这一点的方法有很多,因此研究javadoc和变体(例如FutureTask)并查看示例

    ThreadPoolExecutor executor = (ThreadPoolExecutor)
            Executors.newFixedThreadPool(10);       
    for (;;)  {
        Task task = new Task(...);
         executor.execute(task);
    }
    while (!executor.isTerminated()) { ... }
    executor.shutdown();

创建了一些代码段,您可以在其中尝试将实际处理代码放入

我的测试数据如下所示:

try (PrintWriter pw = new PrintWriter("testdata.txt")) {
    for (int i = 0; i < 1000000; i++)
        pw.println(i);
}
其中,
line
是输入文件的一行,
pw
是输出文件的
PrintWriter

在实际代码中:

try (PrintWriter pw = new PrintWriter("testresult.txt");
        BufferedReader br = new BufferedReader(new FileReader("testdata.txt"))) {
    String line;
    while ((line = br.readLine()) != null)
        pw.println(Integer.parseInt(line) * 2);
}
这是一种可以写得更短的内容,并且流式传输可能更具可读性:

try (PrintWriter pw = new PrintWriter("testresult.txt")) {
    Files.lines(Paths.get("testdata.txt")).forEach(
        line -> pw.println(Integer.parseInt(line) * 2));
}
这两个代码片段产生非常相似的执行时间,在我的机器上大约为1.6-1.7秒(使用“预算”方法测量,
long start=System.currentTimeMillis();
before和
System.out.println(System.currentTimeMillis()-start);
before和
System.out.println(System.currentTimeMillis()-start);
after)

然后,可以使用一个
.parallel()
内部的

try (PrintWriter pw = new PrintWriter("testresult.txt")) {
    Files.lines(Paths.get("testdata.txt")).parallel().forEach(
        line -> pw.println(Integer.parseInt(line) * 2));
}
这将产生混合订单结果。
关于
println(int)
的旁注:它没有文档化,但是它的实际实现是,但是如果您想绝对“安全”并且只构建文档化的功能,您应该自己同步它:

try (PrintWriter pw = new PrintWriter("testresult.txt")) {
    Files.lines(Paths.get("testdata.txt")).parallel().forEach(line -> {
        synchronized (pw) {
            pw.println(Integer.parseInt(line) * 2);
        }
    });
}
它们实际上都比顺序同步慢(2秒和2.2秒,额外的手动同步确实很重要),但是这个处理步骤非常简单当然很重要。因此,重要的是要记住,如果文件操作在您的案例中也占用了时间,那么并行性并不能真正起到帮助作用

为了进行比较,使用线程池的完整代码段:

ExecutorService es = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
ExecutorCompletionService<String> ecs = new ExecutorCompletionService<String>(es);
int counter=0;
try (BufferedReader br = new BufferedReader(new FileReader("testdata.txt"))) {
    String line;
    while ((line = br.readLine()) != null) {
        final String current = line;
        ecs.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                return Integer.toString(Integer.parseInt(current)*2);
            }
        });
        counter++;
    }
}
try (PrintWriter pw = new PrintWriter("testresult.txt")) {
    while(counter>0) {
        pw.println(ecs.take().get());
        counter--;
    }
}
es.shutdown();
ExecutorService es=Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
ExecutorCompletionService ecs=新的ExecutorCompletionService;
int计数器=0;
try(BufferedReader br=newbufferedreader(newfilereader(“testdata.txt”)){
弦线;
而((line=br.readLine())!=null){
最终串电流=线路;
ecs.submit(新的可调用(){
@凌驾
公共字符串调用()引发异常{
返回Integer.toString(Integer.parseInt(当前)*2);
}
});
计数器++;
}
}
try(PrintWriter pw=newprintWriter(“testresult.txt”)){
而(计数器>0){
println(ecs.take().get());
计数器--;
}
}
es.shutdown();
这无疑是其中最长的一个,另一方面,它运行2秒,因此与
同步的
更少的流样本相当,没有它是“安全的”,因为文件操作都发生在主线程中(w
ExecutorService es = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
ExecutorCompletionService<String> ecs = new ExecutorCompletionService<String>(es);
int counter=0;
try (BufferedReader br = new BufferedReader(new FileReader("testdata.txt"))) {
    String line;
    while ((line = br.readLine()) != null) {
        final String current = line;
        ecs.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                return Integer.toString(Integer.parseInt(current)*2);
            }
        });
        counter++;
    }
}
try (PrintWriter pw = new PrintWriter("testresult.txt")) {
    while(counter>0) {
        pw.println(ecs.take().get());
        counter--;
    }
}
es.shutdown();