1、Fork-Join
fork/join是java7更新的一个新的轻量级任务执行框架,其主要目的是要更好滴利用底层平台上的多核CPU和多处理器来进行并行处理,解决问题时通常采用分治(divide and conquer)算法或map/reduce算法来进行。
fork操作是把一个大问题划分为若干较小的问题,一般是递归进行。
join操作是把部分解收集并组织起来,得到最终的完整解,也可能是递归进行的。
如果某个子问题由于等待另外一个子问题的完成而无法继续运行,那么处理该子问题的线程会主动寻找其他尚未运行的子问题来执行,减少了线程等待时间,提高了性能。
ForkJoinTask的子类:RecursiveTask(可返回结果)与RecursiveAction。
ForkJoinPool实现了ExecutorService接口。
public class ForkJoinDemo {
private static final int RANGE_LENGTH = 2000;
private final ForkJoinPool forkJoinPool = new ForkJoinPool();
private static class MaxValueTask extends RecursiveTask<Long> {
private final long[] array;
private final int start;
private final int end;
MaxValueTask(long[] array, int start, int end) {
this.array = array;
this.start = start;
this.end = end;
}
protected Long compute() {
long max = Long.MIN_VALUE;
if (end - start <= RANGE_LENGTH) {
for (int i = start; i < end; i++) {
if (array[i] > max) {
max = array[i];
}
}
} else {
int mid = (start + end) / 2;
MaxValueTask lowTask = new MaxValueTask(array, start, mid);
MaxValueTask highTask = new MaxValueTask(array, mid, end);
lowTask.fork();
highTask.fork();
max = Math.max(max, lowTask.join());
max = Math.max(max, highTask.join());
}
return max;
}
}
public void calculate(long[] array) {
MaxValueTask task = new MaxValueTask(array, 0, array.length);
Long result = forkJoinPool.invoke(task);
System.out.println(result);
}
public void calculateNormal(long[] array) {
long max = Long.MIN_VALUE;
for (int i = 0, n = array.length; i < n; i++) {
if (array[i] > max) {
max = array[i];
}
}
System.out.println(max);
}
public static void main(String[] args) {
Random random = new Random();
int size = Integer.MAX_VALUE / 256;
long[] array = new long[size];
for (int i = 0; i < size; i++) {
array[i] = random.nextLong();
}
ForkJoinDemo mv = new ForkJoinDemo();
long startTime = System.currentTimeMillis();
mv.calculate(array);
long midTime = System.currentTimeMillis();
System.out.println(midTime - startTime);
mv.calculateNormal(array);
long endTime = System.currentTimeMillis();
System.out.println(endTime - midTime);
}
}
这个例子,单从性能上比普通的方式要低很多,因为多线程带来的额外开销过大。但是fork/join可发挥作用的场景还是很多的,比如在目录里头的所有文本文件里头搜索某个关键词。
2、Phaser
Phaser类的特点是把多个线程协作执行的任务划分成多个阶段(phase),在每个阶段上都可以有任意个参与者参与。线程可以随时注册并参与到某个阶段的执行中来。当一个阶段中所有的线程都成功完成之后,Phaser类的对象会自动进入下一个阶段,如此循环下去,直到Phaser类的对象中不再包含任何参与者,此时它会自动结束。功能强大,可以替代CountDownLatch和CyclicBarrier。
Phaser的构造器可指定初始的参与者的个数。
(1)register
动态添加参与者
(2)arriveAndAwaitAdvance
完成之后等待其他参与者完成,会阻塞直到Phaser类的对象成功进入下一个阶段
(3)arriveAndDeregister
执行完成之后取消自己的注册,不参与下一个阶段的执行
public class PhaserDemo {
private final Phaser phaser = new Phaser(1);
private final Pattern imageUrlPattern = Pattern.compile("src=['\"]?(.*?(\\.jpg|\\.gif|\\.png))['\"]?[\\s>]+", Pattern.CASE_INSENSITIVE);
public void download(URL url, final Path path, Charset charset) throws IOException {
if (charset == null) {
charset = StandardCharsets.UTF_8;
}
String content = getContent(url, charset);
List<URL> imageUrls = extractImageUrls(content);
for (final URL imageUrl : imageUrls) {
phaser.register();
new Thread() {
public void run() {
//等待其他线程创建完成
phaser.arriveAndAwaitAdvance();
//进入图片下载阶段
try {
InputStream is = imageUrl.openStream();
Files.copy(is, getSavedPath(path, imageUrl), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
e.printStackTrace();
} finally {
phaser.arriveAndDeregister();
}
}
}.start();
}
//等待其他下载线程创建完成
phaser.arriveAndAwaitAdvance();
//等待下载阶段的下载线程执行完成
phaser.arriveAndAwaitAdvance();
//下载完成之后注销自己
phaser.arriveAndDeregister();
}
private String getContent(URL url, Charset charset) throws IOException {
InputStream is = url.openStream();
return IOUtils.toString(new InputStreamReader(is, charset.name()));
}
private List<URL> extractImageUrls(String content) {
List<URL> result = new ArrayList<URL>();
Matcher matcher = imageUrlPattern.matcher(content);
while (matcher.find()) {
try {
result.add(new URL(matcher.group(1)));
} catch (MalformedURLException e) {
//忽略
}
}
return result;
}
private Path getSavedPath(Path parent, URL url) {
String urlString = url.toString();
int index = urlString.lastIndexOf("/");
String fileName = urlString.substring(index + 1);
return parent.resolve(fileName);
}
public static void main(String[] args) throws IOException {
URL url = new URL("http://www.baidu.com");
PhaserDemo downloader = new PhaserDemo();
downloader.download(url, Paths.get("imgs"), Charset.forName("GB2312"));
}
}