我想从公开的API中获取数据并将数据插入到数据库中
API返回包含两个对象的JSON:Page对象和Activities数组对象
在Page对象中,可以看到有400个页面,所以我需要调用API 400次并将每个调用的Activity存储到DB中。
由于调用API 400次是非常耗时的,我想使用多线程来提高速度,但是,代码表现得很奇怪,传递的页码看起来不按顺序排列,在控制台中我几乎立即看到索引达到numberOfPages 400。
我试过了,我不确定会出现什么问题:
private static Map<Integer, ActivityResult> activities = new ConcurrentHashMap<Integer, ActivityResult>();
public static void fetchActivities(){
ExecutorService es = Executors.newFixedThreadPool(20);
String activityResponse = runRequest("/activity/", first_page);
ActivityResult ar = (ActivityResult) gson.fromJson(activityResponse, ActivityResult.class);
activities.put(first_page, ar);
int numberOfPages = ar.getPaging().getPages();
AtomicInteger index = new AtomicInteger(first_page +1);
for(index.get(); index.get() < numberOfPages; index.incrementAndGet()){
es.submit(new Runnable() {
@Override
public void run() {
System.out.println(index.get());
String tmpResponse = runRequest("/activity/", index.get());
activities.put(index.get(), gson.fromJson(tmpResponse, ActivityResult.class));
}
});
}
es.shutdown();
System.out.println(activities.size());
}
runRequest方法使用okhttp3调用API,它应该是线程安全的。
答案 0 :(得分:2)
你的问题在这里:
AtomicInteger index = new AtomicInteger(first_page +1);
for(index.get(); index.get() < numberOfPages; index.incrementAndGet()){
es.submit(new Runnable() {
@Override
public void run() {
System.out.println(index.get());
String tmpResponse = runRequest("/activity/", index.get());
activities.put(index.get(), gson.fromJson(tmpResponse, ActivityResult.class));
}
});
}
您没有使用AtomicInteger
的原子性。 for循环在父线程上执行得非常快,并提交所有400个任务。 20将很快开始(但不一定立即),其他380将排队等候。 index
已经一直增加到400.任务已经提交但它们可能无法启动,直到将来某个不确定的点。
然后启动任务并使用index
的最新值。队列中的任何任务(在您的情况下为任务20-400)都可能都在for循环完成后开始,index.get
将为所有这些任务返回400。前20个任务可能会从for-loop中间开始,并会获得一组随机值。
正确的代码看起来应该是这样的(我还将Runnable
转换为lambda):
AtomicInteger index = new AtomicInteger(first_page + 1);
for(int i = first_page; i < numberOfPages; ++i){
es.submit(() -> {
int page = index.incrementAndGet();
System.out.println(page);
String tmpResponse = runRequest("/activity/", page);
activities.put(page , gson.fromJson(tmpResponse, ActivityResult.class));
});
}