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