【问题标题】:How to get the data from the map whenever it is present?如何在地图存在时从地图中获取数据?
【发布时间】:2014-01-14 21:25:59
【问题描述】:

下面是我的工厂代码,它启动后台线程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 它仍在解析数据.. 那么我如何确保当客户第一次调用我们的代码时,一旦解析完成,然后只允许在调用方法中获取变量值。

然后第二次就好了..由于数据会在内存中,只有第一次是问题..

有什么办法吗?

【问题讨论】:

    标签: java multithreading executorservice futuretask scheduledexecutorservice


    【解决方案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();
        }
    }
    

    【讨论】:

    • 非常感谢您的帮助。在这种情况下还有一件事,我的主线程在我从 getPrimaryMapping 检索值时总是会等待,或者它只会在我开始时等待第一次我的应用程序?对不起,如果我问愚蠢的问题。这是我第一次使用 CountDownLatch,所以不太了解。如果你能解释一下这在我目前的情况下是如何工作的,我将能够理解 CountDownLatch 是如何工作的。谢谢。跨度>
    • 您的主线程只会等待第一次(countDown() 会将初始值 1 减为零,一旦达到零,await() 将让所有等待线程继续,所有CountDownLatch方法恢复为什么都不做)。如果对你有用,你能接受答案吗?
    • 谢谢范。我刚刚接受了你的回答。非常感谢您的帮助。我只是一个随机的问题。这也适用于同步方法吗?假设我使用同步的 getter 和 setter 方法而不是使用 CountDownLatch。那么它会起作用吗?
    • 不,在这种情况下同步的 getter 和 setter 将不起作用。
    猜你喜欢
    • 2016-11-15
    • 2019-07-03
    • 2014-02-03
    • 2020-08-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多