当数据可用时,如何从地图获取数据?

时间:2014-01-15 00:27:43

标签: java multithreading thread-safety executorservice scheduledexecutorservice

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

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的数据。同时,它将进入call类的Task方法。以下行将抛出异常,为什么? bcoz我的后台线程仍在解析数据,但它没有设置该变量。

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

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

1 个答案:

答案 0 :(得分:2)

您只是想让任务等到第一次更新Map之后再继续?

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是一个Checked Exception可能只是在启动多线程引擎之前将初始值加载到Map中.....