Java 防多线程重复保存数据

大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!今天我们来讨论一下在Java中如何防止多线程重复保存数据。在并发编程中,如果多个线程同时对同一份数据进行操作,可能会导致数据重复保存的问题。本文将通过代码示例详细介绍几种常见的解决方法,包括使用synchronized关键字、ReentrantLock、以及数据库的唯一约束等。

1. 使用synchronized关键字

synchronized关键字是Java中最简单的同步机制,它可以确保同一时刻只有一个线程可以执行某段代码。下面是一个使用synchronized关键字防止多线程重复保存数据的示例。

package cn.juwatech.threads;

import java.util.HashSet;
import java.util.Set;

public class SynchronizedSaveTask implements Runnable {
    private static final Set<String> savedData = new HashSet<>();
    private final String data;

    public SynchronizedSaveTask(String data) {
        this.data = data;
    }

    @Override
    public void run() {
        saveData(data);
    }

    private synchronized void saveData(String data) {
        if (!savedData.contains(data)) {
            savedData.add(data);
            System.out.println("Thread " + Thread.currentThread().getName() + " saved data: " + data);
            // 这里可以添加实际的数据保存逻辑,例如保存到数据库
        } else {
            System.out.println("Thread " + Thread.currentThread().getName() + " data already saved: " + data);
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(new SynchronizedSaveTask("data" + (i % 5))).start();
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.

2. 使用ReentrantLock

ReentrantLock提供了与synchronized类似的功能,但它更加灵活,可以手动控制锁的获取和释放。下面是一个使用ReentrantLock防止多线程重复保存数据的示例。

package cn.juwatech.threads;

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockSaveTask implements Runnable {
    private static final Set<String> savedData = new HashSet<>();
    private static final Lock lock = new ReentrantLock();
    private final String data;

    public LockSaveTask(String data) {
        this.data = data;
    }

    @Override
    public void run() {
        saveData(data);
    }

    private void saveData(String data) {
        lock.lock();
        try {
            if (!savedData.contains(data)) {
                savedData.add(data);
                System.out.println("Thread " + Thread.currentThread().getName() + " saved data: " + data);
                // 这里可以添加实际的数据保存逻辑,例如保存到数据库
            } else {
                System.out.println("Thread " + Thread.currentThread().getName() + " data already saved: " + data);
            }
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(new LockSaveTask("data" + (i % 5))).start();
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.

3. 使用ConcurrentHashMap

ConcurrentHashMap是线程安全的Map实现,可以使用它来存储已经保存过的数据,防止重复保存。下面是一个使用ConcurrentHashMap防止多线程重复保存数据的示例。

package cn.juwatech.threads;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class ConcurrentMapSaveTask implements Runnable {
    private static final ConcurrentMap<String, Boolean> savedData = new ConcurrentHashMap<>();
    private final String data;

    public ConcurrentMapSaveTask(String data) {
        this.data = data;
    }

    @Override
    public void run() {
        saveData(data);
    }

    private void saveData(String data) {
        if (savedData.putIfAbsent(data, Boolean.TRUE) == null) {
            System.out.println("Thread " + Thread.currentThread().getName() + " saved data: " + data);
            // 这里可以添加实际的数据保存逻辑,例如保存到数据库
        } else {
            System.out.println("Thread " + Thread.currentThread().getName() + " data already saved: " + data);
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(new ConcurrentMapSaveTask("data" + (i % 5))).start();
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.

4. 数据库的唯一约束

除了在Java代码中进行同步控制,还可以利用数据库的唯一约束来防止重复保存数据。在数据库表中对需要唯一的数据字段添加唯一约束,当插入重复数据时,数据库会抛出异常。

假设我们有一个保存用户信息的表users,其中email字段必须唯一:

CREATE TABLE users (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(255) NOT NULL,
    email VARCHAR(255) NOT NULL UNIQUE
);
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

在Java代码中处理数据库插入异常:

package cn.juwatech.threads;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class DatabaseSaveTask implements Runnable {
    private final String name;
    private final String email;

    public DatabaseSaveTask(String name, String email) {
        this.name = name;
        this.email = email;
    }

    @Override
    public void run() {
        saveData(name, email);
    }

    private void saveData(String name, String email) {
        String url = "jdbc:mysql://localhost:3306/your_database";
        String username = "your_username";
        String password = "your_password";

        try (Connection connection = DriverManager.getConnection(url, username, password)) {
            String sql = "INSERT INTO users (name, email) VALUES (?, ?)";
            try (PreparedStatement statement = connection.prepareStatement(sql)) {
                statement.setString(1, name);
                statement.setString(2, email);
                statement.executeUpdate();
                System.out.println("Thread " + Thread.currentThread().getName() + " saved data: " + email);
            }
        } catch (SQLException e) {
            if (e.getErrorCode() == 1062) { // Duplicate entry error code for MySQL
                System.out.println("Thread " + Thread.currentThread().getName() + " data already saved: " + email);
            } else {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(new DatabaseSaveTask("User" + (i % 5), "user" + (i % 5) + "@example.com")).start();
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.

通过这些方法,我们可以有效防止多线程重复保存数据,确保数据的一致性和完整性。

微赚淘客系统3.0小编出品,必属精品,转载请注明出处!