fork/join框架的应用场景:可以把一个大任务分割成无数个小任务进行执行,最后汇总完成执行大任务的效果。
案例场景:把pdf转成图片,客户上传pdf,然后用图片再展示给客户,所以pdf转图片的快慢会严重影响到请求相应的速度和客户体验,此案例使用了俩页pdf为任务的最小单元来加快pdf转图片的效果,具体代码如下:
package com.zqsign.file.external.utils;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.apache.pdfbox.tools.imageio.ImageIOUtil;
/**
* @ClassName: PdfConvertImgTask
* @Description: pdf转图片
* @author shidebin
* @date 2018年9月7日
*
*/
public class PdfConvertImgTask extends RecursiveTask<Integer>{
private static final long serialVersionUID = 1L;
private static final int THRESHOLD = 2;// 阈值
private int start;
private int end;
//private ThreadLocal<byte[]> ThreadLocal = new ThreadLocal<>();
private byte[] fileBytes;
private AtomicInteger num = null;
public PdfConvertImgTask(int threadnum,int start, int end,byte[] fileBytes) {
this.start = start;
this.end = end;
//ThreadLocal.set(fileBytes);
this.fileBytes = fileBytes;
num = new AtomicInteger(threadnum);
}
public PdfConvertImgTask(int start, int end,byte[] fileBytes) {
this.start = start;
this.end = end;
//ThreadLocal.set(fileBytes);
this.fileBytes = fileBytes;
}
public PdfConvertImgTask(int start, int end) {
this.start = start;
this.end = end;
}
/**
* @Title: compute
* @Description: pdf转图片
* @param @return 参数
* @author shidebin
* @date 2018年9月7日
* @throws
*/
@Override
protected Integer compute() {
int sum = 0;
// 如果任务足够小就计算任务
boolean canCompute = (end - start) <= THRESHOLD;
if (canCompute) {
PDDocument doc = null;
List<String> imgURIs = new ArrayList<String>();
try {
String imgURI = null;// 图片地址
// doc = PDDocument.load(ThreadLocal.get());
doc = PDDocument.load(fileBytes);
PDFRenderer pdfRenderer = new PDFRenderer(doc);
byte[] fileByte = null;
FileOutputStream file = null;
File file1 = null;
for (int page = start; page < end; ++page) {
ByteArrayOutputStream os = new ByteArrayOutputStream();
BufferedImage bim = pdfRenderer.renderImageWithDPI(page, 150F);
ImageIOUtil.writeImage(bim, "JPG", os);
try {
fileByte = os.toByteArray();
file1 = new File("D:/imgs/"+num.get());
if(!file1.exists()) {
file1.mkdir();
}
file = new FileOutputStream("D:/imgs/"+num.get()+"/"+page+".JPG");
file.write(fileByte);
} finally {
if (os != null) {
os.close();
}
if (file != null) {
file.close();
}
}
imgURIs.add(imgURI);
sum++;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (doc != null)
try {
doc.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} else {
// 如果任务大于阈值,就分裂成两个子任务计算
int middle = (start + end) / 2;
PdfConvertImgTask leftTask = new PdfConvertImgTask(num.get(),start, middle,fileBytes);
PdfConvertImgTask rightTask = new PdfConvertImgTask(num.get(),middle + 1, end,fileBytes);
// 执行子任务
leftTask.fork();
rightTask.fork();
// 等待子任务执行完,并得到其结果
int leftResult = leftTask.join();
int rightResult = rightTask.join();
// 合并子任务
sum = leftResult + rightResult;
}
return sum;
}
public static void main(String[] args) {
for(int i = 0;i<10;i++) {
new Thread(""+i){
public void run() {
ForkJoinPool forkJoinPool = new ForkJoinPool();
long start = System.nanoTime();
File file = new File("D:/Java并发编程的艺术.pdf");
byte[] fileBytes = null;
int pageSize = 0;
PDDocument doc = null;
try {
fileBytes = org.apache.commons.io.IOUtils.toByteArray(new FileInputStream(file));
doc = PDDocument.load(fileBytes);
pageSize = doc.getNumberOfPages();
doc.getPages();
System.out.println(Thread.currentThread().getName()+"========"+pageSize);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (doc != null)
try {
doc.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// 生成一个计算任务,负责计算1+2+3+4
PdfConvertImgTask task = new PdfConvertImgTask(Integer.parseInt(Thread.currentThread().getName()),0, pageSize, fileBytes);
// 执行一个任务
Future<Integer> result = forkJoinPool.submit(task);
if(task.isCompletedAbnormally()) {
try {
throw task.getException();
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try {
System.out.println(Thread.currentThread().getName()+"========"+result.get());
System.out.println(Thread.currentThread().getName()+"========"+(System.nanoTime() - start));
} catch (InterruptedException e) {
} catch (ExecutionException e) {
}
}
}.start();
}
}
}
此案例使用pdf为422,启用了10线程进行转图片,总共运行时间不到2分钟,运行结果如图:
使用到pdf转图片的pom文件的配置:
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.5</version>
</dependency>