java用多线程处理1000万数据,从表里面读取1000万数据,然后做了业务逻辑后,写到文件里面去,如何提高效率,以及避免内存溢出

在Java中使用多线程处理大量数据(如1000万条记录)时,需要特别注意内存管理、线程安全和性能优化。以下是一些建议,帮助你高效地处理这些数据,同时避免内存溢出:

1. 分批处理数据

  • 分页或分批读取:不要一次性从数据库加载所有数据到内存中。可以使用分页查询(如SQL的LIMIT和OFFSET)或游标(Cursor)来分批读取数据。
  • 控制批次大小:根据内存大小和业务逻辑复杂度,合理设置每批处理的数据量。

2. 使用多线程

  • 线程池:使用ExecutorService来管理线程池,可以有效控制线程的数量,避免创建过多线程导致的资源消耗。
  • 任务分配:将读取到的数据分批分配给不同的线程处理。可以使用BlockingQueue来管理待处理的数据队列,每个线程从队列中取出数据进行处理。

3. 缓存和内存管理

  • 使用软引用或弱引用:对于非必须常驻内存的对象,可以考虑使用SoftReferenceWeakReference,这样JVM在内存紧张时会自动回收这些对象。
  • 对象重用:尽可能重用对象,减少对象的创建和销毁。

4. 写入文件

  • 缓冲写入:使用BufferedWriterPrintWriter等缓冲类来写入文件,可以显著提高写入性能。
  • 异步写入:可以考虑使用异步IO(如NIO的AsynchronousFileChannel)来进一步提高文件写入性能。

5. 监控和调优

  • JVM监控:使用JVM监控工具(如VisualVM, JProfiler等)来监控内存使用情况和GC情况,及时调优。
  • 性能分析:使用性能分析工具(如JProfiler, YourKit等)来定位性能瓶颈。

6. 线程安全

  • 同步控制:确保多线程环境下对共享资源的访问是线程安全的。可以使用synchronized关键字、ReentrantLockReadWriteLock等同步机制。
  • 无锁编程:如果可能,尽量使用无锁编程技术(如原子变量、CAS操作等)来提高性能。

7. 示例代码框架

ExecutorService executor = Executors.newFixedThreadPool(10); // 假设使用10个线程  
BlockingQueue<List<Data>> queue = new LinkedBlockingQueue<>();  
  
// 填充队列  
// ...  
  
for (int i = 0; i < 10; i++) {  
    executor.submit(() -> {  
        while (!queue.isEmpty() || !Thread.currentThread().isInterrupted()) {  
            try {  
                List<Data> batch = queue.take(); // 阻塞直到有数据  
                processBatch(batch); // 处理数据  
                writeToFile(batch); // 写入文件  
            } catch (InterruptedException e) {  
                Thread.currentThread().interrupt();  
            }  
        }  
    });  
}  
  
executor.shutdown();  
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);

1. 数据模型(Data 类)

首先,我们需要一个数据模型类来表示从数据库中检索到的每条记录。这个类将包含从数据库表中检索的所有字段。

public class Data {  
    private int id;  
    private String name;  
    // 其他字段...  
  
    // 构造函数  
    public Data(int id, String name) {  
        this.id = id;  
        this.name = name;  
    }  
  
    // Getter 和 Setter  
    public int getId() {  
        return id;  
    }  
  
    public void setId(int id) {  
        this.id = id;  
    }  
  
    public String getName() {  
        return name;  
    }  
  
    public void setName(String name) {  
        this.name = name;  
    }  
  
    // toString 方法(可选,用于调试)  
    @Override  
    public String toString() {  
        return "Data{" +  
               "id=" + id +  
               ", name='" + name + '\'' +  
               '}';  
    }  
}

2. 数据读取(从数据库)

在数据读取部分,我们需要编写代码来连接数据库,执行查询,并将结果集转换为Data对象的列表。

// 假设这是数据读取的方法,它将返回Data对象的批次列表  
private static List<Data> take(Connection conn, int offset, int batchSize) throws SQLException {  
    List<Data> batch = new ArrayList<>();  
    String sql = "SELECT id, name FROM your_table LIMIT ?, ?";  
    try (PreparedStatement stmt = conn.prepareStatement(sql)) {  
        stmt.setInt(1, offset);  
        stmt.setInt(2, offset + batchSize);  
        try (ResultSet rs = stmt.executeQuery()) {  
            while (rs.next() && batch.size() < batchSize) {  
                batch.add(new Data(rs.getInt("id"), rs.getString("name")));  
            }  
        }  
    }  
    return batch;  
}

3. 数据处理

数据处理可以涉及任何形式的业务逻辑,例如验证、转换或聚合数据。

private static void processBatch(List<Data> batch) {  
    // 对batch中的每个Data对象执行操作  
    for (Data data : batch) {  
        // 处理逻辑...  
        System.out.println("Processing: " + data);  
    }  
}

4. 数据写入(到文件)

将处理后的数据写入文件可能涉及将对象序列化为某种格式(如JSON、XML或自定义文本格式)。

private static void writeToFile(List<Data> batch, String filename) {  
    try (BufferedWriter writer = new BufferedWriter(new FileWriter(filename, true))) {  
        for (Data data : batch) {  
            writer.write(data.toString() + "\n");  
        }  
    } catch (IOException e) {  
        e.printStackTrace();  
    }  
}

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值