java stream 并行流停止的一个方案

场景

java的stream很好用,并且还有个并行流。在一些任务计算上,写起来很方便。

并且,还支持batch的分割,可以通过自定义spliterator来实现批量递进,这种情况有时可以起到一些特殊作用(这里不说作用了,到用的时候自己就会知道)。

我的场景是用到并行流,并且是利用到了spliterator。

由于任务量大,所以启动后,我希望可以在一些情况下,停止它。可是stream API没有相应停止接口,所以需要自己想办法解决。

实现

思路

基于spliterator,可以实现。

原因是,spliterator有个trySplit()函数,该函数负责把递进将batch数量的任务交给stream去执行。

利用这一段,我们想办法停止它,就可以终止stream的运行了。

直接返回null起不到作用,所以就想了个取巧的办法,抛出异常。

注意细节

1. 自定义一个runtimeException,在需要停止时,抛出异常,然后外层捕获,随后既可以执行后续逻辑。

2. 线程判断是否需要退出,可以通过一个‘开关’来控制。而并行下需要控制多个线程退出,就需要将开关内用于表达开启或者关闭的变量用volatile关键字修饰,同时为了优化性能,需要用到缓存行填充。

demo

调用stream的代码:Worker

本类是主函数的入口,创建stream并执行。启动时新建了一个线程,在一定时间后将开关标识为‘关闭’。

package com.jfqqqq.test.common.stream;

import java.util.Arrays;
import java.util.Spliterator;
import java.util.concurrent.atomic.LongAdder;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

public class Worker {
    private StopFlag stopController = new StopFlag();

    public void stop() {
        stopController.stop();
    }

    public void test() {

        LongAdder longAdder = new LongAdder();
        Stream<Task> tasks = getTasks(0, 1024, 1, 1022, 10);
//        Stream<Tile> tasks = getTiles4TileEnv(0, 32, 0, 31, 5);//1056
//        Stream<Tile> tasks = getTiles4TileEnv(0, 4, 0, 3, 2);//20
        tasks.parallel().forEach(tile -> {
            System.out.println(tile.getX() + "_" + tile.getY() + "_" + tile.getZ());
            longAdder.increment();
        });
        System.out.println(longAdder.doubleValue());
    }

    public Stream<Task> getTasks(int minx, int maxx, int miny, int maxy, int z) {
        Spliterator<Task> spliterator = IntStream.rangeClosed(minx, maxx).mapToObj(x -> x).flatMap(x -> {
            Stream<Task> stream = IntStream.rangeClosed(miny, maxy).mapToObj(y -> y).map(y -> new Task(x, y, z));
            return stream;
        }).spliterator();
        BatchSpliterator<Task> batchSpliterator = new BatchSpliterator<>(spliterator, 10, stopController);
        return StreamSupport.stream(batchSpliterator, true);
    }

    public static class Task {
        protected int x;
        protected int y;
        protected int z; // 等级

        public Task() {

        }

        public Task(int x, int y, int z) {
            this.x = x;
            this.y = y;
            this.z = z;
        }

        public int getX() {
            return x;
        }

        public void setX(int x) {
            this.x = x;
        }

        public int getY() {
            return y;
        }

        public void setY(int y) {
            this.y = y;
        }

        public int getZ() {
            return z;
        }

        public void setZ(int z) {
            this.z = z;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            Task that = (Task) o;
            return x == that.x && y == that.y && z == that.z;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public int hashCode() {
//		return Objects.hashCode(x, y, z);//为了不依赖google包,这里把google包里的hashcode复制到下面直接计算
            return hashCode(x, y, z);
        }

        /**
         * 从com.google.common.base.Objects粘贴过来的
         *
         * @param objects
         * @return
         */
        private int hashCode(Object... objects) {
            return Arrays.hashCode(objects);
        }

        @Override
        public String toString() {
            return "Tile [x=" + x + ", y=" + y + ", z=" + z + "]";
        }

    }
}

stream的spliterator(递进分割器): BatchSpliterator

/*
 * Copyright (C) 2011 The Baremaps Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package com.jfqqqq.test.common.stream;

import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Consumer;

/**
 * A {@code BatchSpliterator} wraps another spliterator and partition its elements according to a given batch size
 * when trySplit is invoked.
 *
 * @param <T>
 */
public class BatchSpliterator<T> implements Spliterator<T> {

  private final Spliterator<T> spliterator;
  private final int batchSize;
  private StopFlag stopController;

  /**
   * Creates a spliterator that partitions the underlying spliterator according to the given batch size.
   *
   * @param spliterator the underlying spliterator.
   * @param batchSize   the batch size.
   */
  public BatchSpliterator(Spliterator<T> spliterator, int batchSize, StopFlag stopController) {
    this.spliterator = spliterator;
    this.batchSize = batchSize;
    this.stopController = stopController;
  }

  @Override
  public boolean tryAdvance(Consumer<? super T> action) {
    return this.spliterator.tryAdvance(action);
  }

  /**
   * Returns a spliterator covering the elements of a batch.
   *
   * @return a spliterator covering the elements of a batch.
   */
  @Override
  public Spliterator<T> trySplit() {
    if (stopController.isStop()) {//开关关闭了,则抛出异常
      throw new MyRuntimeException();
    }
    HoldingConsumer<T> holder = new HoldingConsumer<>();
    System.out.println(Thread.currentThread().getName()+": trySplit()...");
    if (tryAdvance(holder)) {
      Object[] a = new Object[batchSize];
      int j = 0;
      do {
        a[j] = holder.value();
      } while (++j < batchSize && tryAdvance(holder));
      return Spliterators.spliterator(a, 0, j, characteristics());
    } else {
      return null;
    }
//    return Spliterators.spliterator(new Object[batchSize], 0, j, characteristics());
  }

  /**
   * Returns {@code Long.MAX_VALUE} assuming that the underlying spliterator is of unknown size.
   *
   * @return {@code Long.MAX_VALUE} corresponding to unknown size.
   */
  @Override
  public long estimateSize() {
    return Long.MAX_VALUE;
  }

  /**
   * Returns the characteristics of the underlying spliterator with its ability to be subsized.
   *
   * @return a representation of characteristics.
   */
  @Override
  public int characteristics() {
    return spliterator.characteristics() | SUBSIZED;
  }

}

存储递进结果的容器:HoldingConsumer

/*
 * Copyright (C) 2011 The Baremaps Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package com.jfqqqq.test.common.stream;

import java.util.function.Consumer;

/**
 * A {@code Consumer} that holds the latest value it accepted.
 *
 * @param <T>
 */
public class HoldingConsumer<T> implements Consumer<T> {

  private T value;

  /**
   * {@inheritDoc}
   */
  @Override
  public void accept(T value) {
    this.value = value;
  }

  /**
   * Returns the holded value.
   *
   * @return the holded value.
   */
  public T value() {
    return value;
  }
}

自定义异常:MyRuntimeException

package com.jfqqqq.test.common.stream;

public class MyRuntimeException extends RuntimeException{

}

开关控制器:StopFlag

package com.geovis.vector.clip.relevant;

public class StopController {
    public long p1,p2,p3,p4,p5,p6;//填充缓存行
    private volatile long stop = 0;

    public boolean isStop() {
        return stop == 1;
    }

    public void stop() {
        this.stop = 1;
    }

    public void start() {
        this.stop = 0;
    }
}

完成

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值