我正在使用Java的并发执行器一次读取多个文件,并将信息写入哈希图。这些文件用于日常日志,如下所示:

+-----------+-------+-----------+-------+
| Member ID | Time  | Weight    | Fat % |
+-----------+-------+-----------+-------+
| 1123141   | 1:03  | 162       | 21.2  |
| 5321430   | 1:10  | 131       | 25.3  |
| ...       | ...   | ...       | ...   |
+-----------+-------+-----------+-------+

哈希表将成员名称作为键,对于值,我创建了一个“MemberProperties”类,该类具有时间和其他属性的列表。我试图使用多个线程一次读取多个天,将数据写入哈希图,然后在每个成员的基础上进行数据分析。这是代码的样子
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.ArrayList;
import java.util.Date;
import java.io.IOException;

class MemberProps {
    ArrayList<Date>    time;
    ArrayList<Integer> weight;
    ArrayList<Float>   fat;

    public MemberProps() {
        this.time   = new ArrayList<>();
        this.weight = new ArrayList<>();
        this.fat    = new ArrayList<>();
    }
}

public class MemberFileReader {

    public ConcurrentHashMap<Integer, MemberProps> MemberHash;
    public ExecutorService executor = Executors.newFixedThreadPool(2);

    public void readFiles() {

            //Get this week's files
            File folder = new File("C:\\User\\Temp");
            File[] files = folder.listFiles();

            for (File fl : files) {
                executor.execute(new FileReader(fl.toString()));
            }
            executor.shutdown();
    }

    private final class FileReader implements Runnable {

        private String filepath;

        public FileReader(String filepath) {
            this.filepath = filepath;
        }

        @Override
        public synchronized void run() {

            try {
                String line = null;
                BufferedReader in = new BufferedReader(new FileReader(filepath));
                while ((line = in.readLine()) != null) {

                    MemberProps member = new MemberProps();

                    String[] split_line = line.split("\\t");
                    int member_id = Integer.parseInt(split_line[0]);
                    member.time.add(Date.parse(split_line[1]));
                    member.weight.add(Integer.parseInt(split_line[2]));
                    member.fat.add(Float.parseFloat(split_line[3]));

                    // If not absent, will return member's values
                    member = MemberHash.putIfAbsent(member_id, member);

                    if (member != null){
                        member.time.add(Date.parse(split_line[1]));
                        member.weight.add(Integer.parseInt(split_line[2]));
                        member.fat.add(Float.parseFloat(split_line[3]));
                        MemberHash.put(member_id, member);
                    }
                }

            } catch (IOException E){
                // caught exception
            }
        }
    }
}

我的问题是,由于某种原因,列表中缺少值。例如,对于具有10个日志的成员,我可能有10次但只有9个权重/脂肪,或者类似的9次但有10个权重,依此类推。从调试看来,当不同的线程尝试对get相同时,可能会出现此问题成员和put同时使用,但我不确定。有任何想法吗?为了解决这个问题,我正在考虑为每个线程使用不同的哈希映射,然后再加入映射,但如果有更简单的解决方案,请加入idk。

最佳答案

ArrayList.add不是线程安全的。您应该同步添加到MemberProps中的那些列表,例如

class MemberProps {
    private ArrayList<Date>    time;
    private ArrayList<Integer> weight;
    private ArrayList<Float>   fat;

    public MemberProps() {
        this.time   = new ArrayList<>();
        this.weight = new ArrayList<>();
        this.fat    = new ArrayList<>();
    }

    public synchronized add(Date d, Integer w, Float f) {
        this.time.add(d);
        this.weight.add(d);
        this.fat.add(f);
    }
}

然后使用如下所示的同步方法:
if (member != null)) {
    Date d = Date.parse(split_line[1]);
    Integer w = Integer.parseInt(split_line[2]);
    Float f = Float.parseFloat(split_line[3]);
    member.add(d, w, f);
}

请注意,由于该成员已经存在,因此您不应再次将其放入地图中,而只需将其添加到现有实例中即可。

08-25 08:29