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

获取Java中计划的非阻塞操作的结果,java,concurrency,scheduling,Java,Concurrency,Scheduling,我试图以一种计划的、非阻塞的方式执行一些阻塞操作(比如HTTP请求)。假设我有10个请求,一个请求需要3秒钟,但我不想等待3秒钟,而是等待1秒钟,然后发送下一个请求。在所有执行完成后,我想收集列表中的所有结果并返回给用户 下面是我的场景的原型(线程睡眠用作阻塞操作,而不是HTTP req) publicstaticlist getResults(列表输入)抛出InterruptedException、ExecutionException{ 列表结果=新建LinkedList(); 队列任务=新建

我试图以一种计划的、非阻塞的方式执行一些阻塞操作(比如HTTP请求)。假设我有10个请求,一个请求需要3秒钟,但我不想等待3秒钟,而是等待1秒钟,然后发送下一个请求。在所有执行完成后,我想收集列表中的所有结果并返回给用户

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

publicstaticlist getResults(列表输入)抛出InterruptedException、ExecutionException{
列表结果=新建LinkedList();
队列任务=新建LinkedList();
列表未来=新建LinkedList();
for(整数输入:输入){
可调用任务=新的可调用任务(){
公共整数调用()引发InterruptedException{
睡眠(3000);
返回输入+1000;
}
};
任务。添加(任务);
}
ExecutorService es=Executors.newCachedThreadPool();
ScheduledExecutorService=Executors.newScheduledThreadPool(1);
ses.scheduleAtFixedRate(新的Runnable(){
@凌驾
公开募捐{
可调用任务=tasks.poll();
如果(任务==null){
ses.shutdown();
es.shutdown();
返回;
}
添加(提交(任务));
}
},0,1000,时间单位为毫秒);
while(true){
if(futures.size()==inputs.size()){
for(未来:未来){
整数结果=future.get();
结果。添加(结果);
}
返回结果;
}
}
}
公共静态void main(字符串[]args)引发InterruptedException、ExecutionException{
List results=getResults(新的LinkedList(Arrays.asList(1,2,3,4,5,6,7,8,9,10));
System.out.println(Arrays.toString(results.toArray());
}
我在一个while循环中等待,直到所有任务返回正确的结果。但它从不进入破坏状态,它会无限循环。无论何时我放置一个I/O操作,比如logger,甚至断点,它都会中断while循环,一切都会正常


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

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

while(true){
同步(期货){
if(futures.size()==inputs.size()){
...
}
}
}
这是因为java中的线程使用cpu缓存来提高性能。因此,在同步之前,每个线程都可以有不同的变量值。 因此,我们有更多关于这方面的信息

也可从回答中得出:

这都是关于记忆的。线程通过共享内存进行通信,但是当一个系统中有多个CPU,所有CPU都试图访问同一个内存系统时,内存系统就会成为瓶颈。因此,典型的多CPU计算机中的CPU可以延迟、重新排序和缓存内存操作,以加快速度

当线程之间不进行交互时,这种方法非常有效,但当它们确实想要交互时,就会出现问题:如果线程A将值存储到普通变量中,Java就不能保证线程B何时(甚至是否)会看到值的变化

为了在重要的时候克服这个问题,Java提供了一些同步线程的方法。也就是说,让线程同意程序内存的状态。volatile关键字和synchronized关键字是在线程之间建立同步的两种方法

最后,
futures
列表不会在代码中更新,因为infinte
while
块会持续占用主线程。在while循环中执行任何I/O操作都会给cpu足够的喘息空间来更新其本地缓存


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

非常感谢。我刚刚注意到你的意思。另一个解决方案可能是将
期货
创建为
向量
,这也是线程安全的,而不是LinkedList。关于无限while循环,我同意你的看法。你知道有什么更好的方法来查看列表并返回结果吗。我应该使用
可观察的
?有什么建议吗?我刚刚发现ExecutorService具有等待所有线程完成的函数
waitTermination
。它只是阻塞主线程,直到该ExecutorService管理的所有线程都完成为止。这似乎是一个合适的无限循环。谢谢
    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()));
    }