0

我在我的代码中使用 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类方法中获取数据。我有一个后台线程,它ClientData使用设置器在类中设置数据。

class Task implements Callable<String> {

    public String call() throws Exception {

    //.. some code

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

    //.. some code
    }
}

下面是我的后台线程,它ClientData通过解析来自 URL 的数据来设置我的类中的值,它每 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);

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

4

1 回答 1

2

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

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 中.....

于 2014-01-15T00:48:17.737 回答