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

AKI*_*WEB 2 java multithreading thread-safety atomicity

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

ID      PrimaryKey String
ACCOUNT String
Run Code Online (Sandbox Code Playgroud)

这意味着每个线程应始终使用唯一ID,我也需要IDAccount列中存储相同的ID .因此,如果ID is 1那么在数据库中它应该存储为

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

100 SomeString+100
Run Code Online (Sandbox Code Playgroud)

我总是在Account列中将该userID与该String连接起来.

下面是我的多线程代码,它将产生多个线程 - 每个线程将在每次使用时获得一个新的唯一ID AtomicInteger.它会插入IDID column还追加一条IDAccount

但不知何故,在我的下面的程序中,我在该数据库中看到的是 -

ID Account
1  String+2
2  String+1
3  String+3
Run Code Online (Sandbox Code Playgroud)

哪个不对.它应该像这样 -

ID Account
1  String+1
2  String+2
3  String+3
Run Code Online (Sandbox Code Playgroud)

下面是代码

 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();
    }  
}
Run Code Online (Sandbox Code Playgroud)

以下是我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;
    }


}
Run Code Online (Sandbox Code Playgroud)

谁能告诉我我在这做什么错?我相信它正在发生,因为线程安全问题.多个线程修改userID整数我猜,这就是它错误地写入数据库的原因.

我该如何解决这个问题?

rua*_*akh 5

我看到的主要问题不在于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;
    }
Run Code Online (Sandbox Code Playgroud)

或者只是免除变量:

    public static String getaAccount(int userId) {      
        return "{\"lv\":[{\"v\":{\"userId\":"+userId+"},\"cn\":1}]}";
    }
Run Code Online (Sandbox Code Playgroud)

(你说你做了Constants一成不变的,但是这不是真的.实例Constants将是不可改变的,因为他们根本没有领域;但Constants本身具有公开修改字段,所以这是非常易变的!)

更一般地说,您不应该仅在特定方法中使用临时值字段,并且仅在单次调用期间使用.即使它不是同步问题,也是一个维护问题.例如,Task不需要volatile int userId; userId应该只是其run方法中的局部变量.

另外,我建议将你的东西包装AtomicInteger在自己的类中,IncrementingCounter或者只提供一种叫做(比方说)的方法getNewId.然后getNewId将是唯一必须处理线程之间协调的类.所有其他类可以通过常规技术(不变性,仅存在于单个线程中等)制成线程安全的.