多线程学习笔记十二——Fork/Join框架的使用

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>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值