【问题标题】:How do I get the data from a map when the data is available?当数据可用时,如何从地图中获取数据?
【发布时间】:2014-02-03 07:13:43
【问题描述】:

我在我的代码中使用 Java Callable Future。下面是我使用未来和可调用对象的主要代码 -

public class TimeoutThread {

    public static void main(String[] args) throws Exception {

        // starting the background thread
        new ScheduledCall().startScheduleTask();

        ExecutorService executor = Executors.newFixedThreadPool(5);
        Future<String> future = executor.submit(new Task());

        try {
            System.out.println("Started..");
            System.out.println(future.get(3, TimeUnit.SECONDS));
            System.out.println("Finished!");
        } catch (TimeoutException e) {
            System.out.println("Terminated!");
        }

        executor.shutdownNow();
    }
}

下面是我的Task类,它实现了Callable接口,这个类需要从ClientData类方法中获取数据。我有一个后台线程,它使用 setter 设置 ClientData 类中的数据。

class Task implements Callable<String> {

    public String call() throws Exception {

    //.. some code

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

    //.. some code
    }
}

下面是我的后台线程,它通过解析来自 URL 的数据来设置我的 ClientData 类中的值,它每 10 分钟运行一次。

public class ScheduledCall {

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

    public void startScheduleTask() {

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

    private void callServers() {
        String url = "url";
        RestTemplate restTemplate = new RestTemplate();
        String response = restTemplate.getForObject(url, String.class);
        parseResponse(response);

    }

    // parse the response and set it.
    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);        
    }
}

下面是我的ClientData

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);
    }
}

问题陈述:-

我面临的唯一问题是,每当我第一次启动程序时,会发生什么,它将启动后台线程,该线程将解析来自 URL 的数据。同时,它将进入我的Task 类的call 方法。下面的行会抛出异常,为什么?因为我的后台线程仍在解析数据,它还没有设置那个变量。

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

如何避免这个问题?有没有更好更有效的方法来做到这一点?

【问题讨论】:

    标签: java multithreading thread-safety executorservice scheduledexecutorservice


    【解决方案1】:

    您只想让任务等到地图的第一次更新发生后再继续?

    public class ClientData {
    
        private static final AtomicReference<Map<String, Map<Integer, String>>> primaryMapping = new AtomicReference<>();
        private static final CountDownLatch hasBeenInitialized = new CountDownLatch(1);
    
        public static Map<String, Map<Integer, String>> getPrimaryMapping() {
            try {
                hasBeenInitialized.await();
            } catch (InterruptedException e) {
                throw new IllegalStateException(e);
            }
            return primaryMapping.get();
        }
    
        public static void setPrimaryMapping(Map<String, Map<Integer, String>> map) {
            primaryMapping.set(map);
            hasBeenInitialized.countDown();
        }
    }
    

    一种不会导致同步检查并使您将愚蠢的 InterruptedException 作为检查异常处理的更简单和更有效的方法可能是在启动多线程引擎之前简单地将初始值加载到 Map 中.....

    【讨论】:

    • 谢谢。假设如果我在 ClientData 类中有多个字段,那么我需要在所有这些 getter 和 setter 中按照您在建议中所做的那样做同样的事情?
    • 这是一种选择,是的。虽然不知道程序实际做了什么,但我个人可能只是在那时加载一些初始值。
    • 当然是有道理的。由于某种原因,一旦我启动应用程序,我就无法初始化该值。在这种情况下还有一件事,当我从getPrimaryMapping 检索值时,我的主线程将始终等待,或者它只会在我启动我的应用程序时第一次等待?对不起,如果我问的是愚蠢的问题。这是我第一次与CountDownLatch 合作,所以不太了解。如果你能解释一下这在我目前的情况下如何工作,我将能够理解 CountDownLatch 是如何工作的。谢谢。 .
    • 任何调用 getPrimaryMapping 的线程都会无限期地阻塞,直到其他线程调用 setPrimaryMapping。一次集合至少被调用一次,不会再发生阻塞。
    • 谢谢你。只是一个随机的问题。这也适用于同步方法吗?假设我使用同步的 getter 和 setter 方法而不是使用 CountDownLatch。那么它会起作用吗?
    猜你喜欢
    • 1970-01-01
    • 2016-11-15
    • 2020-08-29
    • 1970-01-01
    • 2020-03-12
    • 1970-01-01
    • 2019-07-03
    • 2019-09-30
    • 1970-01-01
    相关资源
    最近更新 更多