2

以下は、バックグラウンドスレッドを開始する私の工場コードですTempScheduler-

public class TempClientFactory {

    public static IClient getInstance() {
    new TempScheduler().startScheduler();
    return ClientHolder.INSTANCE;
    }

    private static class ClientHolder {
        private static final TempClient INSTANCE = new TempClient();
    }
} 

これで、顧客は上記のファクトリを使用してこのようにコードを呼び出します。彼らは Client インスタンスを一度だけ取得し、そのインスタンスを使用しreadて実装内のメソッドを呼び出します -

IClient client = TempClientFactory.getInstance();
String response = client.read(userId);

以下は、URL からデータを取得し、解析してクラス変数に格納するバックグラウンド スレッド コードです。

public class TempScheduler {

    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

        public void startScheduler() {
            final ScheduledFuture<?> taskHandle = scheduler.scheduleAtFixedRate(new Runnable() {
                public void run() {
                try {
                    callServers();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
                }
            }, 0, 10, TimeUnit.MINUTES);
        }
    }

    // call the servers and get the data and then parse 
    // the response.
    private void callServers() {
        String url = "url";
        RestTemplate restTemplate = new RestTemplate();
        String response = restTemplate.getForObject(url, String.class);
        parseResponse(response);

    }

    // parse the response and store it in a variable
    private void parseResponse(String response) {
        //...       
        ConcurrentHashMap<String, Map<Integer, String>> primaryTables = null;

        //...

        // store the data in ClientData class variables which can be
        // used by other threads
        ClientData.setPrimaryMapping(primaryTables);
    }
}

URL からのデータを解析した後、上記のバックグラウンド スレッドはClientData、セッターを使用してクラスの変数に結果を格納します。以下は私のClientData classです。

public class ClientData {

    private static final AtomicReference<Map<String, Map<Integer, String>>> primaryMapping = new AtomicReference<>();

    public static Map<String, Map<Integer, String>> getPrimaryMapping() {
        return primaryMapping.get();
    }

    public static void setPrimaryMapping(Map<String, Map<Integer, String>> map) {
        primaryMapping.set(map);
    }
}

問題文:-

ここで興味深い部分があります。ご覧のとおり、顧客は上記のファクトリを使用してコードを呼び出し、getInstance()メソッドを呼び出すとすぐに、URL からデータを取得して解析するバックグラウンド スレッドが開始されます。クラス変数に格納します。しかし、getInstance()メソッドが実行された後、彼らはread method私のクライアントコードで即座に呼び出しを行います..

そして、実装コードでバックグラウンド スレッドによって設定された変数を使用することになっています。readメソッドを使用する私の実装コードはCallableTaksfuture.get自動的にcall以下のメソッドになります。以下のクラスでは、バックグラウンド スレッドによって設定された変数値を使用することになっています。getPrimaryMapping以下のコードでメソッドを使用して値を取得する必要があります。

public class ClientTask implements Callable<String> {

    private String userId = null;

    public ClientTask(String userId) {
    this.userId = userId;
    }

    @Override
    public String call() throws Exception {

    //.....

    String hostname = ClientData.getPrimaryMapping("some_string").get(some_number);

    //....

    }   
}

このアプローチの問題は、顧客がインスタンスを取得するために初めてファクトリを呼び出すとすぐに、URL からデータを取得して解析し、データをクラス変数に格納するバックグラウンド スレッドを開始することですが、それはread私の実装クラスのメソッドを即座に呼び出します..

これで初めて、バックグラウンド スレッドがまだデータを解析しているが、実際の呼び出しが 内に来て、メソッドcall methodからデータを取得しようとするが、getPrimaryMapping何もない可能性がありますよね? なぜbcozはまだデータを解析しています..では、解析が完了したら、顧客がコードを呼び出すときはいつでも、呼び出しメソッドで変数値のみを取得できるようにするにはどうすればよいですか。

そして、2回目は大丈夫です..データはメモリにあるので、最初の時間だけが問題です..

これを行う方法はありますか?

4

1 に答える 1

1

これはエレガントな解決策でCountDownLatchはありませんが、トリックを行うことができます:

public class ClientData {

    private static final AtomicReference<Map<String, Map<Integer, String>>> primaryMapping = new AtomicReference<>();
    private static final CountDownLatch firstSet = new CountDownLatch(1);

    public static Map<String, Map<Integer, String>> getPrimaryMapping() {

        try { firstSet.await(); } catch (Exception ignored) {}
        return primaryMapping.get();
    }

    public static void setPrimaryMapping(Map<String, Map<Integer, String>> map) {

        primaryMapping.set(map);
        firstSet.countDown();
    }
}
于 2014-01-14T22:38:44.433 に答える