我在我的代码中使用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);
如何避免此问题?有没有更好更有效的方法来做到这一点?
答案 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中.....