解决Java多线程插入重复数据入库的问题

在实际开发中,当多个线程同时插入数据到数据库中时,很容易出现重复数据的情况。重复数据的产生可能会导致数据库中的数据不准确,影响业务逻辑的正确性。因此,我们需要一种方法来保证多线程插入数据时不会出现重复数据的问题。

问题分析

假设我们有多个线程同时向数据库中插入数据,每个线程都会执行以下代码:

public void insertData(String data) {
    // 检查数据是否已经存在于数据库中
    if (!dataExist(data)) {
        // 插入数据到数据库中
        insertIntoDatabase(data);
    }
}

在上述代码中,我们首先通过 dataExist 方法检查数据库中是否已经存在相同的数据,如果不存在则执行插入操作。

问题的关键在于多个线程同时执行 dataExist 方法时,可能会同时判断数据不存在,然后都执行插入操作,导致重复数据的产生。

因此,为了解决这个问题,我们需要一种机制来保证多个线程同时插入数据时的互斥性。

解决方案

方案一:使用数据库的唯一约束

一种简单的方法是在数据库中为要插入的字段添加唯一约束。例如,如果我们要插入的数据是一个名为 data 的字段,我们可以在数据库中添加一个唯一约束:

ALTER TABLE my_table ADD CONSTRAINT unique_data UNIQUE (data);

这样,当多个线程同时插入数据时,如果数据已经存在于数据库中,数据库会报错并拒绝插入,从而避免了重复数据的产生。

方案二:使用互斥锁

另一种方法是使用互斥锁来保证多线程插入数据时的互斥性。我们可以使用Java中的 synchronized 关键字或者 Lock 接口来实现互斥锁。

下面是使用 synchronized 关键字的示例代码:

private Object lock = new Object();

public void insertData(String data) {
    synchronized (lock) {
        if (!dataExist(data)) {
            insertIntoDatabase(data);
        }
    }
}

在上述代码中,我们使用了一个对象 lock 作为锁。当一个线程进入 synchronized 代码块时,其他线程会被阻塞,直到当前线程执行完毕释放锁。这样,我们就保证了多线程插入数据时的互斥性,避免了重复数据的产生。

方案三:使用数据库事务

还有一种方法是使用数据库的事务来保证多线程插入数据时的一致性。

在使用事务时,我们需要使用数据库连接的 setAutoCommit 方法将自动提交关闭,然后在插入数据后手动提交事务。这样,在一个事务中插入数据后,其他线程插入相同数据时会被阻塞,直到当前事务提交。

下面是使用事务的示例代码:

public void insertData(String data) {
    Connection conn = null;
    PreparedStatement pstmt = null;
    
    try {
        conn = getConnection(); // 获取数据库连接
        conn.setAutoCommit(false); // 关闭自动提交
        
        if (!dataExist(conn, data)) {
            pstmt = conn.prepareStatement("INSERT INTO my_table (data) VALUES (?)");
            pstmt.setString(1, data);
            pstmt.executeUpdate();
            
            conn.commit(); // 手动提交事务
        }
    } catch (SQLException e) {
        if (conn != null) {
            try {
                conn.rollback(); // 出现异常时回滚事务
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        }
    } finally {
        closeConnection(conn, pstmt); // 关闭数据库连接
    }
}

在上述代码中,我们首先通过 getConnection 方法获取数据库连接,并关闭自动提交。然后,如果数据不存在于数据库中,我们执行插入操作,并手动提交事务。如果出现异常,我们回滚事务。最后,我们通过 closeConnection 方法关闭数据库连接。

总结

通过使用唯一约束、互斥锁或数据库事