我需要插入有两列的数据库 -
ID PrimaryKey String
ACCOUNT String
这意味着每个线程总是应该使用唯一ID,我也需要在ID
列中存储相同的Account
。所以假设ID is 1
然后在数据库中它应该存储为
ID Account
1 SomeString+1
2 SomeString+2
3 SomeString+3
....
..
100 SomeString+100
我总是在Account列中将该userID与该String连接起来。
下面是我的多线程代码,它将产生多个线程 - 每次线程都会获得一个新的唯一ID,因为我正在使用AtomicInteger
。它会将ID
插入ID 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
整数的多个线程我猜这就是它错误地写入数据库的原因。
如何解决此问题?
答案 0 :(得分:5)
我看到的主要问题不在于Task.userId
,而在于Constants.A_ACCOUNT
:如果两个单独的线程同时调用getaAccount
,那么它们都会设置{{1}并且两者都读取它,因此它们最终可以具有相同的值,或者每个都具有另一个值,或者诸如此类的东西。要解决此问题,您可以使用局部变量而不是静态字段:
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
的 Instances 将是不可变的,因为它们根本没有字段;但是Constants
本身有一个可公开修改的字段,所以非常可变!)
更一般地说,您不应该仅在特定方法中使用临时值字段,并且仅在单次调用期间使用。即使它不是同步问题,也是一个维护问题。例如,Constants
不需要Task
; volatile int userId
应该只是userId
方法中的局部变量。
另外,我建议将run
包装在自己的类AtomicInteger
或其他类中,只提供一种方法,称为(例如)IncrementingCounter
。然后getNewId
将是唯一必须处理线程之间协调的类。所有其他类都可以通过常规技术(不变性,仅存在于单个线程中等)制作线程安全。
答案 1 :(得分:1)
您正在阅读和修改静态变量而没有来自多个线程的任何同步:A_ACCOUNT
。只需在getaacount()
中将其设为局部变量,一切都应按预期工作。
答案 2 :(得分:1)
考虑到数字的数字也是一个字符串,这个问题在很多年前就解决了:
如果您的密钥无法更改数据类型,请将自动增量值复制到字符串列中。它的数字并不重要 - 它仍然是独一无二的。