Java 异步迭代器

Java 异步迭代器,java,multithreading,asynchronous,concurrency,Java,Multithreading,Asynchronous,Concurrency,我有以下代码: while(slowIterator.hasNext()){ performLengthTask(slowIterator.next()); } 因为迭代器和任务都很慢,所以将它们放在单独的线程中是有意义的。下面是对迭代器包装器的快速而肮脏的尝试: 类AsyncIterator实现迭代器{ 私有最终阻塞队列=新的ArrayBlockingQueue(100); 专用异步迭代器(最终迭代器委托){ 新线程(){ @凌驾 公开募捐{ while(delegate.hasNext()

我有以下代码:

while(slowIterator.hasNext()){
performLengthTask(slowIterator.next());
}
因为迭代器和任务都很慢,所以将它们放在单独的线程中是有意义的。下面是对迭代器包装器的快速而肮脏的尝试:

类AsyncIterator实现迭代器{
私有最终阻塞队列=新的ArrayBlockingQueue(100);
专用异步迭代器(最终迭代器委托){
新线程(){
@凌驾
公开募捐{
while(delegate.hasNext()){
queue.put(delegate.next());//为了简洁起见,try/catch已删除
}
}
}.start();
}
@凌驾
公共布尔hasNext(){
返回true;
}
@凌驾
公共交通工具{
return queue.take();//为了简洁起见,try/catch已删除
}
//…remove()引发UnsupportedOperationException
}
但是,此实现缺少对“hasNext()”的支持。当然,hasNext()方法在知道是否返回true之前是可以阻止的。我可以在我的AsyncIterator中有一个peek对象,我可以将hasNext()更改为从队列中获取一个对象,并让next()返回这个peek。但是,如果已到达委托迭代器的末尾,这将导致hasNext()无限期地阻塞

不用使用ArrayBlockingQueue,我当然可以自己进行线程通信:

私有静态类AsyncIterator实现迭代器{
private final Queue=new LinkedList();
私有布尔delegateDone=false;
专用异步迭代器(最终迭代器委托){
新线程(){
@凌驾
公开募捐{
while(delegate.hasNext()){
final T next=delegate.next();
已同步(AsyncIterator.this){
queue.add(下一步);
AsyncIterator.this.notify();
}
}
已同步(AsyncIterator.this){
delegateDone=true;
AsyncIterator.this.notify();
}
}
}.start();
}
@凌驾
公共布尔hasNext(){
已同步(此){
while(queue.size()==0&&!delegateDone){
试一试{
等待();
}捕捉(中断异常e){
抛出新错误(e);
}
}
}
返回queue.size()>0;
}
@凌驾
公共交通工具{
return queue.remove();
}
@凌驾
公共空间删除(){
抛出新的UnsupportedOperationException();
}
}
然而,所有额外的同步、等待和通知并不能真正提高代码的可读性,而且很容易将竞争条件隐藏在某个地方

还有更好的主意吗

更新 是的,我知道常见的观察者/可观察模式。然而,通常的实现并没有预见到数据流的结束,它们不是迭代器


这里我特别想要一个迭代器,因为实际上上面提到的循环存在于一个外部库中,它需要一个迭代器。

这是一个棘手的问题,但我想这次我得到了正确的答案。(我删除了我的第一个答案。)

答案是使用哨兵。我尚未测试此代码,为了清晰起见,我删除了try/catch:

public class AsyncIterator<T> implements Iterator<T> {

    private BlockingQueue<T> queue = new ArrayBlockingQueue<T>(100);
    private T sentinel = (T) new Object();
    private T next;

    private AsyncIterator(final Iterator<T> delegate) {
        new Thread() {
            @Override
            public void run() {
                while (delegate.hasNext()) {
                    queue.put(delegate.next());
                }
                queue.put(sentinel);
            }
        }.start();
    }

    @Override
    public boolean hasNext() {
        if (next != null) {
            return true;
        }
        next = queue.take(); // blocks if necessary
        if (next == sentinel) {
            return false;
        }
        return true;
    }

    @Override
    public T next() {
        T tmp = next;
        next = null;
        return tmp;
    }

}
公共类AsyncIterator实现迭代器{
private BlockingQueue=new ArrayBlockingQueue(100);
私有T sentinel=(T)新对象();
私人T next;
专用异步迭代器(最终迭代器委托){
新线程(){
@凌驾
公开募捐{
while(delegate.hasNext()){
queue.put(delegate.next());
}
队列。放置(哨兵);
}
}.start();
}
@凌驾
公共布尔hasNext(){
如果(下一步!=null){
返回true;
}
next=queue.take();//必要时阻塞
如果(下一个==哨兵){
返回false;
}
返回true;
}
@凌驾
公共交通工具{
T tmp=下一个;
next=null;
返回tmp;
}
}
这里的要点是hasNext()需要阻塞,直到下一项准备就绪。它还需要某种退出条件,由于线程问题,它不能使用空队列或布尔标志。哨兵解决了这个问题,没有任何锁定或同步


编辑:缓存的“下一步”可以多次调用hasNext()。

或者省去麻烦,使用RxJava:

import java.util.Iterator;

import rx.Observable;
import rx.Scheduler;
import rx.observables.BlockingObservable;
import rx.schedulers.Schedulers;

public class RxAsyncIteratorExample {

    public static void main(String[] args) throws InterruptedException {
        final Iterator<Integer> slowIterator = new SlowIntegerIterator(3, 7300);

        // the scheduler you use here will depend on what behaviour you
        // want but io is probably what you want
        Iterator<Integer> async = asyncIterator(slowIterator, Schedulers.io());
        while (async.hasNext()) {
            performLengthTask(async.next());
        }
    }

    public static <T> Iterator<T> asyncIterator(
            final Iterator<T> slowIterator,
            Scheduler scheduler) {

        final Observable<T> tObservable = Observable.from(new Iterable<T>() {
            @Override
            public Iterator<T> iterator() {
                return slowIterator;
            }
        }).subscribeOn(scheduler);

        return BlockingObservable.from(tObservable).getIterator();
    }

    /**
     * Uninteresting implementations...
     */
    public static void performLengthTask(Integer integer)
            throws InterruptedException {
        log("Running task for " + integer);
        Thread.sleep(10000l);
        log("Finished task for " + integer);
    }

    private static class SlowIntegerIterator implements Iterator<Integer> {
        private int count;
        private final long delay;

        public SlowIntegerIterator(int count, long delay) {
            this.count = count;
            this.delay = delay;
        }

        @Override
        public boolean hasNext() {
            return count > 0;
        }

        @Override
        public Integer next() {
            try {
                log("Starting long production " + count);
                Thread.sleep(delay);
                log("Finished long production " + count);
            }
            catch (InterruptedException e) {
                throw new IllegalStateException(e);
            }
            return count--;
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }
    }

    private static final long startTime = System.currentTimeMillis();

    private static void log(String s) {
        double time = ((System.currentTimeMillis() - startTime) / 1000d);
        System.out.println(time + ": " + s);
    }
}

听起来像是一个典型的生产者/消费者问题,只是您希望每个线程只有一个线程。必须正常使用迭代器,并将任务转储到
执行器服务中。这不需要重新创建抽象。考虑使用RXJAVA():它正是你想要做的。它是一个以称为“Observable”的异步iterable类型为中心的库。它通过一整套转换、聚合和并发功能得到了充分充实。@LouisWasserman:不,我特别需要一个if Iterator实现(更新了我的问题)。@isnot2bad:完全正确。迭代器产生大量I/O负载,而处理产生大量CPU负载。如果我同步的话,我的CPU和硬盘会轮流空转,无所事事,而另一个则处于紧张状态。这看起来很棒!实际上,我考虑过这样的解决方案,但我从未尝试过,因为我不想使用
null
作为哨兵(可能是列表的一部分),我认为
(T)new Object()
肯定会导致ClassCastException,因此从未尝试过。然而,你当然是对的!仔细想想,这是很有道理的。但是这里有一个(可解决的)问题:
hasNext()
可能比
next()
更频繁地被调用。所以:
iterator.hasNext();iterator.hasNext();sysout(iterator.next())
的输出应与iterator.hasNext()相同;sysout(iterator.next())但这是不同的
0.031: Starting long production 3
7.332: Finished long production 3
7.332: Starting long production 2
7.333: Running task for 3
14.633: Finished long production 2
14.633: Starting long production 1
17.333: Finished task for 3
17.333: Running task for 2
21.934: Finished long production 1
27.334: Finished task for 2
27.334: Running task for 1
37.335: Finished task for 1