6

データベースから読み取り専用、非可変、不揮発性のデータをキャッシュおよび取得するのに役立つ静的ヘルパークラスを実装しています。

(剥ぎ取られた)例:

public class CacheHelper
{
    private static HashMap foos, bars;

    public static Foo getFoo(int fooId) { /* etc etc */ }
    public static Bar getBar(int barId) { /* etc etc */ }

    public static void reloadAllCaches()
    {
        //This is where I need it to lock access to all the other static methods
    }
}

静的クラスについて読んだsynchronized方法では、reloadAllCaches()メソッドにキーワードを追加すると、そのメソッドの実行中にクラス全体にロックが適用されます。これは正しいです?(編集:うん、正しくない。回答ありがとうございます。)

注:getterこのデータは変更されないため、メソッドとメソッドが返すオブジェクトのスレッドセーフにとらわれず、できるだけ早く返されるようにしたいと思います。

4

5 に答える 5

7

このキーワードをreloadAllCaches()関数に追加すると、reloadAllCaches()関数の実行中はsynchronized、キーワードを取得したクラス内の他のすべての静的関数を実行できなくなります。synchronized

synchronizedキーワードを取得したかどうかに関係なく、非静的関数を実行できる方法。synchronizedまた、キーワードのない他のすべての関数を実行できます。

結局のところ、を備えた関数は次のsynchronizedように見ることができます:

public class Bar
{
    public static void foo()
    {
        synchronized (Bar.class)
        {
            // your code
        }
    }
}

synchronizedキーワードを使用した非静的関数は、次のように表示されます。

public class Bar
{
    public void foo()
    {
        synchronized (this)
        {
            // your code
        }
    }
}

したがって、静的関数と非静的関数は異なる同期コンテキストを持ち、synchronizedキーワードを使用して相互の実行をブロックしません。

あなたの場合、私はの使用法を提案しますReentrantReadWriteLock。このクラスでは、任意の数の関数が同時に読み取りロックを取得できますが、書き込みロックを取得できるのは1つの関数のみです。書き込みロックは、読み取りロックが設定されていない場合にのみ取得され、書き込みロックが設定されている限り、読み取りロックは取得されません。

リロード関数に書き込みロックをフェッチさせ、すべての読み取り関数に書き込みロックをフェッチさせることができます。ofcauseの静的インスタンスを使用する必要がReentrantReadWriteLockあります。

私の提案は、次のように実装することです。

public class CacheHelper
{
    private static HashMap foos, bars;
    private static java.util.concurrent.locks.ReadWriteLock lock = new java.util.concurrent.locks.ReentrantReadWriteLock();

    public static Foo getFoo(int fooId)
    {
        lock.readLock().lock();
        try {
            /* etc etc */
        } finally {
            lock.readLock().unlock();
        }
    }
    public static Bar getBar(int barId)
    {
        lock.readLock().lock();
        try {
            /* etc etc */
        } finally {
            lock.readLock().unlock();
        }
    }

    public static void reloadAllCaches()
    {
        lock.writeLock().lock();
        try {
            //This is where I need it to lock access to all the other static methods
        } finally {
            lock.writeLock().unlock();
        }
    }
}
于 2012-07-18T13:26:29.620 に答える
2

いいえ、これは正しくありません。synchronizedメソッドのみに追加するreloadAllCachesということは、そのメソッドの呼び出し元がクラスのロックを取得する必要があることを意味しますが、同期されていないメソッドを呼び出すスレッドは引き続きクラスにアクセスできます。これを安全に行うには、アクセサを同じロックで同期する必要があります。そうしないと、リーダースレッドが最新の変更を認識できず、古いデータを取得する可能性があります。または、ConcurrentHashMapを使用することもできます。

于 2012-07-18T13:19:17.557 に答える
2

コレクションをロックせずに再入力できるようにする場合は、コレクションを不変のコレクションに置き換えます。

private static volatile Map foos, bars;

public static Foo getFoo(int fooId) { return foos.get(fooId); }
public static Bar getBar(int barId) { /* etc etc */ }

public static void reloadAllCaches()
{
    Map newFoo = ...
    // populate newFoo
    foos = newFoo;

    Map newBar = ...
    // populate newBar
    bars = newBar;
}

getFooは、マップが常に置き換えられ、変更されることはないため、ロックを必要とせずに完全に一貫したコピーを表示します。


synchronizedメソッドではなくオブジェクトをロックします。この場合、CacheHelper.classオブジェクトをロックしています。

ゲッターをできるだけ速くするために、使用する代わりにConcurrentHashMapを使用できますsynchronized


更新にのみ同期を使用する例。

final ConcurrentMap<Key, ExpensiveObject> map =

public ExpensiveObject getOrNull(Key key) { 
     return map.get(key); 
}

public ExpensiveObject getOrCreate(Key key) {
     synchronized(map) {
         ExpensiveObject ret = map.get(key);
         if (ret == null)
              map.put(key, ret = new ExpensiveObject(key));
         return ret;
     }
}
于 2012-07-18T13:35:43.257 に答える
0

CacheHelperクラスオブジェクト(CacheHelper.class)にロックを適用するのではなくreloadAllCaches()、コードの一部にこのメソッド内でこのロックを適用できます。これは、表示されるすべてのメソッドがすべてであり、すべてのメソッドstaticを作成するとsynchronized、いずれかのスレッドがアクセスしている場合にすべてのスレッドがブロックされるためです。方法。

于 2012-07-18T13:24:32.023 に答える
0

簡単に言うと、ロックは他のスレッドが同じメソッドを同時に実行するのを防ぐだけであり、静的またはその他のクラスの他のコンテンツに対してロックリソースを提供しません。

他のスレッドは、制御を持つスレッドがそのメソッドを終了するまで、メソッドへのアクセスをブロックします。他のものへのアクセスは、すべてのスレッドで引き続き無料です。

オブジェクト自体をロック制御する必要がある場合は、スレッドセーフなアクセサーまたはキャッシュのある種の継承処理を提供することを検討する必要があります。

つまり、このメソッド内に新しいキャッシュを構築し、構築したら、キャッシュヘルパー内の参照オブジェクトをそれらの新しいオブジェクトに置き換えた場合、reloadAllCachesメソッドを同期するだけで十分です。

ただし、既存のキャッシュコンテナを再利用/リサイクルする場合は、キャッシュが破棄および再構築されている間の読み取りを防ぐために、コンテナレベルでロックを使用する必要があります。

(例のように)複数のキャッシュされたマップをリロードする必要がある場合は、キャッシュされたオブジェクトを別のレイヤーで抽象化する必要がある場合があります。そうしないと、再適用時にキャッシュへの同期アクセスが失われる可能性があります。

于 2012-07-18T13:33:16.270 に答える