如何确保int为该线程分配了一个唯一的id



我需要插入到有两列的数据库中-

ID      PrimaryKey String
ACCOUNT String

因此,这意味着每个线程应该始终使用唯一的id,并且我还需要在Account列中存储相同的ID。所以假设如果ID is 1,那么在数据库中它应该被存储为

ID  Account
1   SomeString+1
2   SomeString+2
3   SomeString+3
....
..
100 SomeString+100

我将该userID与"帐户"列中的"字符串"连接起来。

下面是我的多线程代码,它将生成多个线程——每次使用AtomicInteger时,每个线程都会获得一个新的唯一ID。它将插入IDID column,并将ID附加到Account

但不知怎么的,在我下面的程序中,我在数据库中看到的是-

ID Account
1  String+2
2  String+1
3  String+3

这是不对的。应该是这样的

ID Account
1  String+1
2  String+2
3  String+3

以下是代码

 public static void main(String[] args) {
        final int noOfThreads = 4;
        final int noOfTasks = 10;
        final AtomicInteger id = new AtomicInteger(1);
        ExecutorService service = Executors.newFixedThreadPool(noOfThreads);
        for (int i = 0; i < noOfTasks * noOfThreads; i++) {
            service.submit(new Task(id));
        }
    }

class Task implements Runnable {
    private final AtomicInteger id;
    private volatile int userId;
    public Task(AtomicInteger id) {
        this.id = id;
    }

    @Override
    public void run() {
        dbConnection = getDBConnection();
        preparedStatement = dbConnection.prepareStatement(Constants.INSERT_ORACLE_SQL);
        userId = id.getAndIncrement();
        preparedStatement.setString(1, String.valueOf(userId));
        preparedStatement.setString(2, Constants.getaAccount(userId));
        preparedStatement.executeUpdate();
    }  
}

下面是我的Constants class,我已经使它不可变了。

public final class Constants {
    public static String A_ACCOUNT;
    public final static String INSERT_ORACLE_SQL = "INSERT INTO XMP_TEST"
        + "("
        + "ID, A_ACCOUNT) VALUES"
        + "(?, ?)";

    public static String getaAccount(int userId) {      
        A_ACCOUNT = "{"lv":[{"v":{"userId":"+userId+"},"cn":1}]}";
        return A_ACCOUNT;
    }

}

有人能告诉我我在这里做错了什么吗?我相信这是因为线程安全问题。我想是多个线程修改了userID整数,这就是为什么它被错误地写入数据库的原因。

如何解决此问题?

我看到的主要问题不是Task.userId,而是Constants.A_ACCOUNT:如果两个单独的线程同时调用getaAccount,那么它们都会设置Constants.A_ACCOUNT并读取它,这样它们最终可能都具有相同的值,或者每个都具有另一个的值,等等。要解决此问题,可以使用局部变量而不是静态字段:

    public static String getaAccount(int userId) {      
        final String ret = "{"lv":[{"v":{"userId":"+userId+"},"cn":1}]}";
        return ret;
    }

或者干脆去掉变量:

    public static String getaAccount(int userId) {      
        return "{"lv":[{"v":{"userId":"+userId+"},"cn":1}]}";
    }

(你说你已经使Constants不可变,但事实并非如此。Constants实例将是不可变的,因为它们根本没有字段;但Constants本身有一个可公开修改的字段,所以它非常可变!)

更一般地说,不应该只在特定方法中使用字段作为临时值,也不应该仅在对其进行一次调用时使用字段。即使这不是同步问题,也是维护问题。例如,Task不需要volatile int userIduserId应该只是其run方法中的一个局部变量。

此外,我建议将AtomicInteger封装在它自己的类IncrementingCounter或其他类中,该类只提供一个方法,称为(比如)getNewId。那么getNewId将是唯一一个必须处理线程之间协调的类。所有其他类都可以通过常规技术(不变性、仅存在于单个线程中等)实现线程安全

您正在读取和修改一个静态变量,而不需要来自多个线程的任何同步:A_ACCOUNT。只要将其作为getaacount()中的局部变量,一切都应该按预期工作。

考虑到数字的数字也是字符串,这个问题在多年前就得到了解决:

  • 将自动递增列添加到父表
  • 现在插入父记录将为您提供一个唯一的编号
  • 你没有说你在使用哪个数据库,但每个数据库都有一种检索刚刚插入的自动增量值的方法,所以检索它并将其用作你的唯一值

如果不能更改键的数据类型,也可以将自动递增值复制到字符串列中。它是数字的并不重要,它仍然是独一无二的。

相关内容

  • 没有找到相关文章

最新更新