获取Java中计划的非阻塞操作的结果

时间:2019-06-17 09:21:20

标签: java concurrency scheduling

我正在尝试以计划的非阻塞方式进行一些阻塞操作(例如HTTP请求)。假设我有10个请求,一个请求耗时3秒,但我不想等待3秒,而是等待1秒发送下一个请求。完成所有执行后,我希望将所有结果收集到列表中并返回给用户。

下面是我的场景的原型(线程睡眠用作阻塞操作,而不是HTTP请求)。

    public static List<Integer> getResults(List<Integer> inputs) throws InterruptedException, ExecutionException {
        List<Integer> results = new LinkedList<Integer>();
        Queue<Callable<Integer>> tasks = new LinkedList<Callable<Integer>>();
        List<Future<Integer>> futures = new LinkedList<Future<Integer>>();
        for (Integer input : inputs) {
            Callable<Integer> task = new Callable<Integer>() {
                public Integer call() throws InterruptedException {
                    Thread.sleep(3000);
                    return input + 1000;
                }
            };
            tasks.add(task);
        }

        ExecutorService es = Executors.newCachedThreadPool();
        ScheduledExecutorService ses = Executors.newScheduledThreadPool(1);
        ses.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                Callable<Integer> task = tasks.poll();
                if (task == null) {
                    ses.shutdown();
                    es.shutdown();
                    return;
                }
                futures.add(es.submit(task));
            }
        }, 0, 1000, TimeUnit.MILLISECONDS);

        while(true) {
            if(futures.size() == inputs.size()) {
                for (Future<Integer> future : futures) {
                    Integer result = future.get();
                    results.add(result);
                }
                return results;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        List<Integer> results = getResults(new LinkedList<Integer>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)));
        System.out.println(Arrays.toString(results.toArray()));
    }

我正在等待while循环,直到所有任务都返回正确的结果。但是它永远不会进入破坏条件,并且会无限循环。每当我进行诸如logger或断点之类的I / O操作时,它都会中断while循环,一切都会好起来。

我对Java并发还比较陌生,试图了解正在发生的事情以及这是否是正确的方法。我想I / O操作会在线程调度程序上触发某些事件,并使其检查集合的大小。

1 个答案:

答案 0 :(得分:1)

您需要同步线程。您有两个不同的线程(主线程和exectuor服务线程)访问futures列表,并且由于LinkedList不同步,因此这两个线程看到futures的两个不同值。

while(true) {
  synchronized(futures) {
    if(futures.size() == inputs.size()) {
      ...
    }
  }
}

发生这种情况是因为Java中的线程使用cpu缓存来提高性能。因此,每个线程在同步之前可能具有不同的变量值。 该SO question对此有更多信息。

也来自this答案:

  

所有与内存有关。线程通过共享内存进行通信,但是当系统中有多个CPU时,所有CPU都试图访问同一个内存系统,那么内存系统将成为瓶颈。因此,允许典型的多CPU计算机中的CPU进行延迟,重新排序和缓存操作,以加快处理速度。

     

这在线程之间不进行交互时很有效,但是当它们实际上要进行交互时会引起问题:如果线程A将值存储到一个普通变量中,则Java无法保证线程何时(或即使) B会看到值的变化。

     

为了克服重要的问题,Java为您提供了一些同步线程的方法。也就是说,使线程在程序内存的状态上达成共识。 volatile关键字和synced关键字是在线程之间建立同步的两种方法。

最后,futures列表不会在您的代码中更新,因为由于无限while块的存在,主线程一直被占用。在while循环中执行任何I / O操作都会为cpu提供足够的呼吸空间来更新其本地缓存。

无限的while循环通常是一个坏主意,因为它非常占用资源。在下一次迭代之前添加一个小的延迟可以使它更好一些(尽管效率仍然很低)。

相关问题