3

X秒ごとにメモリ内の変更された値をDBに書き戻すゲームがあります。これらの値は、保持しているデータが編集されると、コンテナー (HashMaps および ArrayLists) に格納されます。

簡単にするために、DB に書き込むコンテナーが 1 つしかないとします。

public static HashMap<String, String> dbEntitiesDeletesBacklog = new HashMap<String, String>();

私のDB書き込みループ:

Timer dbUpdateJob = new Timer();
dbUpdateJob.schedule(new TimerTask() {
    public void run() {
        long startTime = System.nanoTime();
        boolean updateEntitiesTableSuccess = UpdateEntitiesTable();
        if (!updateEntitiesTableSuccess){
            try {
                conn.rollback();
            } catch (SQLException e) {
                e.printStackTrace();
                logger.fatal(e.getMessage());
                System.exit(1);
            }
        } else { //everything saved to DB - commit time
            try {
                conn.commit();
            } catch (SQLException e) {
                e.printStackTrace();
                logger.fatal(e.getMessage());
                System.exit(1);
            }
        }
        logger.debug("Time to save to DB: " + (System.nanoTime() - startTime) / 1000000 + " milliseconds");
    }
}, 0, 10000); //TODO:: figure out the perfect saving delay

私の更新方法:

private boolean UpdateEntitiesTable() {
    Iterator<Entry<String, String>> it = dbEntitiesDeletesBacklog.entrySet().iterator();
    while (it.hasNext()) {
        Entry<String, String> pairs = it.next();
        String tmpEntityId = pairs.getKey();

        int deletedSuccess = UPDATE("DELETE" + 
                " FROM " + DB_NAME + ".entities" + 
                " WHERE entity_id=(?)", new String[]{tmpEntityId});
        if (deletedSuccess != 1) {
            logger.error("Entity " + tmpEntityId + " was unable to be deleted.");
            return false;
        }
        it.remove();
        dbEntitiesDeletesBacklog.remove(tmpEntityId);
    }

この抜粋に含まれていない dbEntitiesDeletesBacklog HashMap およびその他のコンテナーの「DB への保存」中に、ある種のロック メカニズムを作成する必要がありますか? イテレータを作成してからループするので、必要だと思います。イテレータが作成された後、エントリをループする前に何かが追加された場合はどうなるでしょうか。申し訳ありませんが、これはプロセスの質問であり、コードのヘルプの質問ではありません (非常に多くのサンプル コードが含まれているため)。

私が使用する他のコンテナについても同じ質問:

public static ArrayList<String> dbCharacterDeletesBacklog =  new ArrayList<String>();

private boolean DeleteCharactersFromDB() {
    for (String deleteWho : dbCharacterDeletesBacklog){
        int deleteSuccess = MyDBSyncher.UPDATE("DELETE FROM " + DB_NAME + ".characters" +
                " WHERE name=(?)", 
                new String[]{deleteWho});

        if (deleteSuccess != 1) {
            logger.error("Character(deleteSuccess): " + deleteSuccess);
            return false;
        }
    }
    dbCharacterDeletesBacklog.clear();
    return true;
}

いつものように、これについて助けてくれてありがとう。それは大歓迎です!!

4

2 に答える 2

2

少なくとも、Collections.synchronizedMapマップに同時にアクセスしている場合は、( 経由で) 同期されたマップが必要です。そうしないと、非決定論的な動作が発生する可能性があります。

それ以上に、あなたが示唆するように、反復中にマップをロックする必要もあります。Collections.synchronizedMap()提案のjavadocからは次のとおりです。

コレクション ビューのいずれかを反復処理する場合、返されたマップでユーザーが手動で同期することが不可欠です。

Map m = Collections.synchronizedMap(new HashMap());
      ...
Set s = m.keySet();  // Needn't be in synchronized block
      ...
synchronized(m) {  // Synchronizing on m, not s!
    Iterator i = s.iterator(); // Must be in synchronized block
    while (i.hasNext())
        foo(i.next());
}

このアドバイスに従わないと、非決定的な動作が発生する可能性があります。

ConcurrentHashMapまたは、通常の HashMap の代わりに a を使用して、反復中の同期を不要にします。ゲームの場合、コレクションを長期間ロックすることを避けるため、これはおそらくより良いオプションです。

おそらくさらに良いことですが、データベースを更新するたびにコレクションを取得し、すべての新しい更新が書き込まれる新しい空のコレクションに置き換えるように、新しいコレクションをローテーションすることを検討してください。データベースの書き込み中にコレクションがロックされるのを回避します。この場合のコレクションは、このグラブと置換をスレッドセーフにするために、何らかのコンテナーによって管理されます。<<< 注: この場合、基になるコレクションを変更コードに公開することはできません。これは、スワップを有効にする (競合状態を発生させない) ために、その参照を厳密に非公開にする必要があるためです。

于 2013-08-12T20:57:01.887 に答える
0

これが私が使用するもののサンプルです。同様の問題を抱えている他の誰かに役立つことを期待して、ここに投稿しています。

public class MyDBSyncher {

    public static boolean running = false;
    public static HashMap<String, String> dbEntitiesInsertsBacklog_A = new HashMap<String, String>();
    public static HashMap<String, String> dbEntitiesInsertsBacklog_B = new HashMap<String, String>();

    public MyDBSyncher(){
        Timer dbUpdateJob = new Timer();
        dbUpdateJob.schedule(new TimerTask() {
            public void run() {
                running = true;
                boolean updateEntitiesTableSuccess = UpdateEntitiesTable();
                running = false;
            }
        }, 0, 10000); //TODO:: figure out the perfect saving delay
    }

    public HashMap getInsertableEntitiesHashMap(){
        if (running){
            return dbEntitiesInsertsBacklog_B;
        } else {
            return dbEntitiesInsertsBacklog_A;
        }
    }

    private boolean UpdateEntitiesTable() {
        Iterator<Entry<String, String>> it2 = getInsertableEntitiesHashMap().entrySet().iterator();
        while (it2.hasNext()) {
            Entry<String, String> pairs = it2.next();
            String tmpEntityId = pairs.getKey();

            //some DB updates here

            it2.remove();
            getInsertableEntitiesHashMap().remove(tmpEntityId);
        }
        return true;
    }
}
于 2013-08-15T19:54:10.230 に答える