场景
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;
}
}