2

2つの列があるデータベースに挿入する必要があります-

ID      PrimaryKey String
ACCOUNT String

つまり、各スレッドは常に一意のIDを使用する必要があり、同じものIDAccount列に格納する必要があります。したがってID is 1、データベースに保存する必要がある場合は、

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

100 SomeString+100

そのuserIDを常にAccount列のその文字列と連結しています。

以下は、複数のスレッドを生成するマルチスレッドコードです-そして、各スレッドは、そのために使用しているたびに、新しい一意のIDを取得しAtomicIntegerます。そしてそれをに挿入し、IDそれを列ID columnに追加しますIDAccount

しかし、どういうわけか、以下のプログラムで、そのデータベースで見たものは-

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整数を変更していると思います。そのため、データベースに誤って書き込まれています。

この問題を解決するにはどうすればよいですか?

4

3 に答える 3

5

私が目にする主な問題はTask.userIdではなく にありConstants.A_ACCOUNTます: 2 つの別々のスレッド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 userIduserIdrun

AtomicIntegerまた、独自のクラス、IncrementingCounterまたは (say) と呼ばれるメソッドを 1 つだけ提供する何かでラップすることをお勧めしますgetNewId。次にgetNewId、スレッド間の調整を処理する必要がある唯一のクラスになります。他のすべてのクラスは、通常の手法 (不変性、単一スレッド内にのみ存在するなど) によってスレッドセーフにすることができます。

于 2013-02-07T23:00:12.430 に答える
1

複数のスレッドからの同期を行わずに、静的変数を読み取って変更しています: A_ACCOUNT. ローカル変数にするだけでgetaacount()、すべてが意図したとおりに機能するはずです。

于 2013-02-07T23:00:45.190 に答える
1

数値の桁も文字列であることを考えると、この問題は何年も前に解決されました。

  • 親テーブルに自動インクリメント列を追加する
  • 親レコードを挿入すると、一意の番号が得られます
  • どのデータベースを使用しているかはわかりませんが、すべての DB には挿入されたばかりの自動インクリメントの値を取得する方法があるため、それを取得して一意の値として使用します。

キーのデータ型を変更できない場合は、自動インクリメント値も文字列列にコピーします。それが数値であることは問題ではありません - それはまだユニークです。

于 2013-02-08T00:03:23.500 に答える