打开idea,新建一个moudle。
选择Spring Initializr,单击next,进入下一步。
接下来,安装Spring Boot套路设置项目名称和包名。
单击next。
再单击next。
单击finish完成工程的创建。工程目录结构为:
修改pom.xml,添加pdf和Spring等相关依赖
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.2.5.RELEASEversion>
<relativePath/>
parent>
<groupId>com.njustgroupId>
<artifactId>pdfmutithreadartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>pdfmutithreadname>
<description>Demo project for Spring Bootdescription>
<properties>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-freemarkerartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
<version>RELEASEversion>
<scope>compilescope>
dependency>
<dependency>
<groupId>com.itextpdfgroupId>
<artifactId>itextpdfartifactId>
<version>5.5.13version>
dependency>
<dependency>
<groupId>com.itextpdf.toolgroupId>
<artifactId>xmlworkerartifactId>
<version>5.5.13version>
dependency>
<dependency>
<groupId>commons-iogroupId>
<artifactId>commons-ioartifactId>
<version>2.5version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
<plugin>
<artifactId>maven-compiler-pluginartifactId>
<configuration>
<source>${java.version}source>
<target>${java.version}target>
configuration>
plugin>
plugins>
build>
project>
首先完成资源文件的创建和配置,新建如下文件。
freemarker.ftl
,这里的模板为了后面和多线程版本测试做对比,将PDF设置的比较大,由于数据比较简单。暂时使用数量模拟现实中的质量。说白了就是模拟一段耗时操作。
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Titletitle>
<style>
body {
font-family: SimHei;
}
.pos {
position: absolute;
left: 100px;
top: 150px
}
style>
head>
<body>
<div class="blue pos">
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">水调歌头·明月几时有h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">宋代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">苏轼a>p>
<div class="contson" id="contson632c5beb84eb">
<p>丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。p>
<p>明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。p>
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">清平乐·别来春半h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">五代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">李煜a>p>
<div class="contson" id="contson632c5beb84eb">
别来春半,触目柔肠断。砌下落梅如雪乱,拂了一身还满。(柔肠断 一作:愁肠断) 雁来音信无凭,路遥归梦难成。离恨恰如春草,更行更远还生。
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">水调歌头·明月几时有h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">宋代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">苏轼a>p>
<div class="contson" id="contson632c5beb84eb">
<p>丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。p>
<p>明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。p>
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">清平乐·别来春半h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">五代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">李煜a>p>
<div class="contson" id="contson632c5beb84eb">
别来春半,触目柔肠断。砌下落梅如雪乱,拂了一身还满。(柔肠断 一作:愁肠断) 雁来音信无凭,路遥归梦难成。离恨恰如春草,更行更远还生。
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">水调歌头·明月几时有h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">宋代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">苏轼a>p>
<div class="contson" id="contson632c5beb84eb">
<p>丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。p>
<p>明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。p>
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">清平乐·别来春半h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">五代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">李煜a>p>
<div class="contson" id="contson632c5beb84eb">
别来春半,触目柔肠断。砌下落梅如雪乱,拂了一身还满。(柔肠断 一作:愁肠断) 雁来音信无凭,路遥归梦难成。离恨恰如春草,更行更远还生。
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">水调歌头·明月几时有h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">宋代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">苏轼a>p>
<div class="contson" id="contson632c5beb84eb">
<p>丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。p>
<p>明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。p>
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">清平乐·别来春半h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">五代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">李煜a>p>
<div class="contson" id="contson632c5beb84eb">
别来春半,触目柔肠断。砌下落梅如雪乱,拂了一身还满。(柔肠断 一作:愁肠断) 雁来音信无凭,路遥归梦难成。离恨恰如春草,更行更远还生。
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">水调歌头·明月几时有h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">宋代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">苏轼a>p>
<div class="contson" id="contson632c5beb84eb">
<p>丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。p>
<p>明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。p>
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">清平乐·别来春半h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">五代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">李煜a>p>
<div class="contson" id="contson632c5beb84eb">
别来春半,触目柔肠断。砌下落梅如雪乱,拂了一身还满。(柔肠断 一作:愁肠断) 雁来音信无凭,路遥归梦难成。离恨恰如春草,更行更远还生。
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">水调歌头·明月几时有h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">宋代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">苏轼a>p>
<div class="contson" id="contson632c5beb84eb">
<p>丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。p>
<p>明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。p>
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">清平乐·别来春半h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">五代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">李煜a>p>
<div class="contson" id="contson632c5beb84eb">
别来春半,触目柔肠断。砌下落梅如雪乱,拂了一身还满。(柔肠断 一作:愁肠断) 雁来音信无凭,路遥归梦难成。离恨恰如春草,更行更远还生。
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">水调歌头·明月几时有h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">宋代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">苏轼a>p>
<div class="contson" id="contson632c5beb84eb">
<p>丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。p>
<p>明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。p>
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">清平乐·别来春半h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">五代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">李煜a>p>
<div class="contson" id="contson632c5beb84eb">
别来春半,触目柔肠断。砌下落梅如雪乱,拂了一身还满。(柔肠断 一作:愁肠断) 雁来音信无凭,路遥归梦难成。离恨恰如春草,更行更远还生。
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">水调歌头·明月几时有h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">宋代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">苏轼a>p>
<div class="contson" id="contson632c5beb84eb">
<p>丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。p>
<p>明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。p>
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">清平乐·别来春半h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">五代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">李煜a>p>
<div class="contson" id="contson632c5beb84eb">
别来春半,触目柔肠断。砌下落梅如雪乱,拂了一身还满。(柔肠断 一作:愁肠断) 雁来音信无凭,路遥归梦难成。离恨恰如春草,更行更远还生。
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">水调歌头·明月几时有h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">宋代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">苏轼a>p>
<div class="contson" id="contson632c5beb84eb">
<p>丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。p>
<p>明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。p>
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">清平乐·别来春半h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">五代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">李煜a>p>
<div class="contson" id="contson632c5beb84eb">
别来春半,触目柔肠断。砌下落梅如雪乱,拂了一身还满。(柔肠断 一作:愁肠断) 雁来音信无凭,路遥归梦难成。离恨恰如春草,更行更远还生。
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">水调歌头·明月几时有h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">宋代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">苏轼a>p>
<div class="contson" id="contson632c5beb84eb">
<p>丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。p>
<p>明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。p>
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">清平乐·别来春半h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">五代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">李煜a>p>
<div class="contson" id="contson632c5beb84eb">
别来春半,触目柔肠断。砌下落梅如雪乱,拂了一身还满。(柔肠断 一作:愁肠断) 雁来音信无凭,路遥归梦难成。离恨恰如春草,更行更远还生。
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">水调歌头·明月几时有h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">宋代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">苏轼a>p>
<div class="contson" id="contson632c5beb84eb">
<p>丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。p>
<p>明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。p>
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">清平乐·别来春半h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">五代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">李煜a>p>
<div class="contson" id="contson632c5beb84eb">
别来春半,触目柔肠断。砌下落梅如雪乱,拂了一身还满。(柔肠断 一作:愁肠断) 雁来音信无凭,路遥归梦难成。离恨恰如春草,更行更远还生。
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">水调歌头·明月几时有h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">宋代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">苏轼a>p>
<div class="contson" id="contson632c5beb84eb">
<p>丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。p>
<p>明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。p>
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">清平乐·别来春半h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">五代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">李煜a>p>
<div class="contson" id="contson632c5beb84eb">
别来春半,触目柔肠断。砌下落梅如雪乱,拂了一身还满。(柔肠断 一作:愁肠断) 雁来音信无凭,路遥归梦难成。离恨恰如春草,更行更远还生。
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">水调歌头·明月几时有h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">宋代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">苏轼a>p>
<div class="contson" id="contson632c5beb84eb">
<p>丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。p>
<p>明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。p>
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">清平乐·别来春半h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">五代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">李煜a>p>
<div class="contson" id="contson632c5beb84eb">
别来春半,触目柔肠断。砌下落梅如雪乱,拂了一身还满。(柔肠断 一作:愁肠断) 雁来音信无凭,路遥归梦难成。离恨恰如春草,更行更远还生。
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">水调歌头·明月几时有h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">宋代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">苏轼a>p>
<div class="contson" id="contson632c5beb84eb">
<p>丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。p>
<p>明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。p>
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">清平乐·别来春半h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">五代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">李煜a>p>
<div class="contson" id="contson632c5beb84eb">
别来春半,触目柔肠断。砌下落梅如雪乱,拂了一身还满。(柔肠断 一作:愁肠断) 雁来音信无凭,路遥归梦难成。离恨恰如春草,更行更远还生。
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">水调歌头·明月几时有h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">宋代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">苏轼a>p>
<div class="contson" id="contson632c5beb84eb">
<p>丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。p>
<p>明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。p>
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">清平乐·别来春半h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">五代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">李煜a>p>
<div class="contson" id="contson632c5beb84eb">
别来春半,触目柔肠断。砌下落梅如雪乱,拂了一身还满。(柔肠断 一作:愁肠断) 雁来音信无凭,路遥归梦难成。离恨恰如春草,更行更远还生。
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">水调歌头·明月几时有h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">宋代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">苏轼a>p>
<div class="contson" id="contson632c5beb84eb">
<p>丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。p>
<p>明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。p>
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">清平乐·别来春半h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">五代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">李煜a>p>
<div class="contson" id="contson632c5beb84eb">
别来春半,触目柔肠断。砌下落梅如雪乱,拂了一身还满。(柔肠断 一作:愁肠断) 雁来音信无凭,路遥归梦难成。离恨恰如春草,更行更远还生。
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">水调歌头·明月几时有h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">宋代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">苏轼a>p>
<div class="contson" id="contson632c5beb84eb">
<p>丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。p>
<p>明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。p>
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">清平乐·别来春半h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">五代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">李煜a>p>
<div class="contson" id="contson632c5beb84eb">
别来春半,触目柔肠断。砌下落梅如雪乱,拂了一身还满。(柔肠断 一作:愁肠断) 雁来音信无凭,路遥归梦难成。离恨恰如春草,更行更远还生。
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">水调歌头·明月几时有h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">宋代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">苏轼a>p>
<div class="contson" id="contson632c5beb84eb">
<p>丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。p>
<p>明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。p>
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">清平乐·别来春半h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">五代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">李煜a>p>
<div class="contson" id="contson632c5beb84eb">
别来春半,触目柔肠断。砌下落梅如雪乱,拂了一身还满。(柔肠断 一作:愁肠断) 雁来音信无凭,路遥归梦难成。离恨恰如春草,更行更远还生。
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">水调歌头·明月几时有h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">宋代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">苏轼a>p>
<div class="contson" id="contson632c5beb84eb">
<p>丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。p>
<p>明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。p>
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">清平乐·别来春半h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">五代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">李煜a>p>
<div class="contson" id="contson632c5beb84eb">
别来春半,触目柔肠断。砌下落梅如雪乱,拂了一身还满。(柔肠断 一作:愁肠断) 雁来音信无凭,路遥归梦难成。离恨恰如春草,更行更远还生。
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">水调歌头·明月几时有h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">宋代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">苏轼a>p>
<div class="contson" id="contson632c5beb84eb">
<p>丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。p>
<p>明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。p>
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">清平乐·别来春半h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">五代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">李煜a>p>
<div class="contson" id="contson632c5beb84eb">
别来春半,触目柔肠断。砌下落梅如雪乱,拂了一身还满。(柔肠断 一作:愁肠断) 雁来音信无凭,路遥归梦难成。离恨恰如春草,更行更远还生。
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">水调歌头·明月几时有h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">宋代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">苏轼a>p>
<div class="contson" id="contson632c5beb84eb">
<p>丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。p>
<p>明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。p>
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">清平乐·别来春半h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">五代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">李煜a>p>
<div class="contson" id="contson632c5beb84eb">
别来春半,触目柔肠断。砌下落梅如雪乱,拂了一身还满。(柔肠断 一作:愁肠断) 雁来音信无凭,路遥归梦难成。离恨恰如春草,更行更远还生。
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">水调歌头·明月几时有h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">宋代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">苏轼a>p>
<div class="contson" id="contson632c5beb84eb">
<p>丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。p>
<p>明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。p>
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">清平乐·别来春半h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">五代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">李煜a>p>
<div class="contson" id="contson632c5beb84eb">
别来春半,触目柔肠断。砌下落梅如雪乱,拂了一身还满。(柔肠断 一作:愁肠断) 雁来音信无凭,路遥归梦难成。离恨恰如春草,更行更远还生。
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">水调歌头·明月几时有h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">宋代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">苏轼a>p>
<div class="contson" id="contson632c5beb84eb">
<p>丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。p>
<p>明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。p>
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">清平乐·别来春半h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">五代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">李煜a>p>
<div class="contson" id="contson632c5beb84eb">
别来春半,触目柔肠断。砌下落梅如雪乱,拂了一身还满。(柔肠断 一作:愁肠断) 雁来音信无凭,路遥归梦难成。离恨恰如春草,更行更远还生。
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">水调歌头·明月几时有h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">宋代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">苏轼a>p>
<div class="contson" id="contson632c5beb84eb">
<p>丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。p>
<p>明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。p>
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">清平乐·别来春半h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">五代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">李煜a>p>
<div class="contson" id="contson632c5beb84eb">
别来春半,触目柔肠断。砌下落梅如雪乱,拂了一身还满。(柔肠断 一作:愁肠断) 雁来音信无凭,路遥归梦难成。离恨恰如春草,更行更远还生。
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">水调歌头·明月几时有h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">宋代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">苏轼a>p>
<div class="contson" id="contson632c5beb84eb">
<p>丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。p>
<p>明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。p>
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">清平乐·别来春半h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">五代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">李煜a>p>
<div class="contson" id="contson632c5beb84eb">
别来春半,触目柔肠断。砌下落梅如雪乱,拂了一身还满。(柔肠断 一作:愁肠断) 雁来音信无凭,路遥归梦难成。离恨恰如春草,更行更远还生。
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">水调歌头·明月几时有h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">宋代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">苏轼a>p>
<div class="contson" id="contson632c5beb84eb">
<p>丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。p>
<p>明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。p>
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">清平乐·别来春半h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">五代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">李煜a>p>
<div class="contson" id="contson632c5beb84eb">
别来春半,触目柔肠断。砌下落梅如雪乱,拂了一身还满。(柔肠断 一作:愁肠断) 雁来音信无凭,路遥归梦难成。离恨恰如春草,更行更远还生。
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">水调歌头·明月几时有h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">宋代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">苏轼a>p>
<div class="contson" id="contson632c5beb84eb">
<p>丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。p>
<p>明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。p>
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">清平乐·别来春半h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">五代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">李煜a>p>
<div class="contson" id="contson632c5beb84eb">
别来春半,触目柔肠断。砌下落梅如雪乱,拂了一身还满。(柔肠断 一作:愁肠断) 雁来音信无凭,路遥归梦难成。离恨恰如春草,更行更远还生。
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">水调歌头·明月几时有h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">宋代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">苏轼a>p>
<div class="contson" id="contson632c5beb84eb">
<p>丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。p>
<p>明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。p>
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">清平乐·别来春半h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">五代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">李煜a>p>
<div class="contson" id="contson632c5beb84eb">
别来春半,触目柔肠断。砌下落梅如雪乱,拂了一身还满。(柔肠断 一作:愁肠断) 雁来音信无凭,路遥归梦难成。离恨恰如春草,更行更远还生。
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">水调歌头·明月几时有h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">宋代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">苏轼a>p>
<div class="contson" id="contson632c5beb84eb">
<p>丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。p>
<p>明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。p>
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">清平乐·别来春半h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">五代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">李煜a>p>
<div class="contson" id="contson632c5beb84eb">
别来春半,触目柔肠断。砌下落梅如雪乱,拂了一身还满。(柔肠断 一作:愁肠断) 雁来音信无凭,路遥归梦难成。离恨恰如春草,更行更远还生。
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">水调歌头·明月几时有h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">宋代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">苏轼a>p>
<div class="contson" id="contson632c5beb84eb">
<p>丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。p>
<p>明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。p>
div>
<h1 style="font-size:20px; line-height:22px; height:22px; margin-bottom:10px;">清平乐·别来春半h1>
<p class="source"><a href="https://blog.csdn.net/qq_32510597/article/details/105060870">五代a><span>:span><a
href="https://blog.csdn.net/qq_32510597/article/details/105054876">李煜a>p>
<div class="contson" id="contson632c5beb84eb">
别来春半,触目柔肠断。砌下落梅如雪乱,拂了一身还满。(柔肠断 一作:愁肠断) 雁来音信无凭,路遥归梦难成。离恨恰如春草,更行更远还生。
div>
div>
body>
html>
application.yml
配置模板路径及生成文件位置
DEST: D:/xxxx/pdfOut/
HTML: freemarker.ftl
FONT: simhei.ttf
至此资源配置完成
package com.njust.pdfmutithread.user;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.tool.xml.XMLWorkerFontProvider;
import com.itextpdf.tool.xml.XMLWorkerHelper;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import java.io.*;
import java.nio.charset.Charset;
import java.util.Map;
@Service
public class PDFService {
private String font = "simhei.ttf";
private static Configuration freemarkerCfg = null;
@Value("${DEST}")
private String dest;
@Value("${HTML}")
private String html;
public String getFont() {
return font;
}
public void setFont(String font) {
this.font = font;
}
public static Configuration getFreemarkerCfg() {
return freemarkerCfg;
}
public static void setFreemarkerCfg(Configuration freemarkerCfg) {
PDFService.freemarkerCfg = freemarkerCfg;
}
public String getDest() {
return dest;
}
public void setDest(String dest) {
this.dest = dest;
}
public String getHtml() {
return html;
}
public void setHtml(String html) {
this.html = html;
}
public void createPdf(String content, String dest) throws IOException, DocumentException {
// step 1
Document document = new Document();
FileOutputStream fileOutputStream = new FileOutputStream(dest);
// step 2
PdfWriter writer = PdfWriter.getInstance(document, fileOutputStream);
// step 3
document.open();
// step 4
XMLWorkerFontProvider fontImp = new XMLWorkerFontProvider(XMLWorkerFontProvider.DONTLOOKFORFONTS);
fontImp.register(font);
XMLWorkerHelper.getInstance().parseXHtml(writer, document,
new ByteArrayInputStream(content.getBytes("UTF-8")), null, Charset.forName("UTF-8"), fontImp);
// step 5
document.close();
}
/**
* freemarker渲染html
*/
public String freeMarkerRender(Map<String, Object> data, String htmlTmp) {
Writer out = new StringWriter();
try {
// 获取模板,并设置编码方式
setFreemarkerCfg();
Template template = freemarkerCfg.getTemplate(htmlTmp);
template.setEncoding("UTF-8");
//将合并后的数据和模板写入到流中,这里使用的字符流
template.process(data, out);
out.flush();
return out.toString();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
return null;
}
/**
* 设置freemarkerCfg
*/
private void setFreemarkerCfg() {
freemarkerCfg = new Configuration();
//freemarker的模板目录
try {
freemarkerCfg.setDirectoryForTemplateLoading(new ClassPathResource("template").getFile());
} catch (IOException e) {
e.printStackTrace();
}
}
}
这里的get set方法,主要为了方便测试的时候调用。
如上图所示,PDFService 的主要方法是createPdf:创建PDF文件。
freeMarkerRender:freemarker渲染html。setFreemarkerCfg:设置freemarkerCfg。然后新建一个task任务操作类。如下图所示:
PDFTaskSingThread
负责流水线创建PDF
package com.njust.pdfmutithread.user.task;
import com.itextpdf.text.DocumentException;
import com.njust.pdfmutithread.user.PDFService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* @author Chen
* @version 1.0
* @date 2020/3/26 22:09
* @description:
*/
@Component
public class PDFTaskSingThread {
@Autowired
private PDFService pdfService;
public void createOne() {
//需要填充的数据
Map<String, Object> data = new HashMap<>(16);
String content = pdfService.freeMarkerRender(data, pdfService.getHtml());
String temp = pdfService.getDest() + "a.pdf";
//创建pdf
try {
pdfService.createPdf(content, temp);
} catch (IOException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
}
}
public void createManyWithOneThread(int num) {
//需要填充的数据
Map<String, Object> data = new HashMap<>(16);
String content = pdfService.freeMarkerRender(data, pdfService.getHtml());
if (num <= 0) {
num = 10;
}
String temp = null;
for (int i = 0; i < num; i++) {
temp = pdfService.getDest() + i + ".pdf";
//创建pdf
try {
pdfService.createPdf(content, temp);
} catch (IOException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
}
}
}
}
在PDFService中新建测试类
PDFService
主要根据JOB_LENGTH大小测试批量生产PDF花费的时间。
package com.njust.pdfmutithread.user;
import com.njust.pdfmutithread.user.task.PDFTaskSingThread;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Chen
* @version 1.0
* @date 2020/3/26 21:55
* @description:
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class PDFServiceTest {
@Autowired
private PDFTaskSingThread pdfTaskSingThread;
public static void printTime(long start, long end) {
long spendTime = end - start;
System.out.println("spendTime: " + spendTime);
}
@Test
public void createOne() {
long start = System.currentTimeMillis();
pdfTaskSingThread.createOne();
long end = System.currentTimeMillis();
printTime(start, end);
}
// JOB_LENGTH:260 spendTime: 17310
// JOB_LENGTH:512 spendTime: 30660
// JOB_LENGTH:1024 spendTime: 59573
@Test
public void createManyWithOneThread() {
long start = System.currentTimeMillis();
pdfTaskSingThread.createManyWithOneThread(JOB_LENGTH);
long end = System.currentTimeMillis();
printTime(start, end);
}
private final static String JOB_NAME = "生成PDF";
private final static int JOB_LENGTH = 1024;
}
不同的JOB_LENGTH花费的时间在作者本人的电脑上运行的时间已经写在了注释里面。读者也可以在自己电脑上运行。
对具体流程不是很熟悉的读者可以根据上面的时序图辅助理解。
ITaskProcesser
package com.njust.pdfmutithread.system.vo;
/**
*
*
*类说明:要求框架使用者实现的任务接口,因为任务的性质在调用时才知道,
*所以传入的参数和方法的返回值均使用泛型
*/
public interface ITaskProcesser<T, R> {
/**
* @param data 调用方法需要使用的业务数据
* @return 方法执行后业务方法的结果
*/
TaskResult<R> taskExecute(T data);
}
ItemVo
package com.njust.pdfmutithread.system.vo;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
/**
* 类说明:存放到队列的元素
*/
public class ItemVo<T> implements Delayed {
private long activeTime;//到期时间,单位毫秒
private T date;
//activeTime是个过期时长
public ItemVo(long activeTime, T date) {
super();
this.activeTime = TimeUnit.NANOSECONDS.convert(activeTime,
TimeUnit.MILLISECONDS) + System.nanoTime();//将传入的时长转换为超时的时刻
this.date = date;
}
public long getActiveTime() {
return activeTime;
}
public T getDate() {
return date;
}
//按照剩余时间排序
@Override
public int compareTo(Delayed o) {
long d = getDelay(TimeUnit.NANOSECONDS) - o.getDelay(TimeUnit.NANOSECONDS);
return (d == 0) ? 0 : ((d > 0) ? 1 : -1);
}
//返回元素的剩余时间
@Override
public long getDelay(TimeUnit unit) {
long d = unit.convert(this.activeTime - System.nanoTime(),
TimeUnit.NANOSECONDS);
return d;
}
}
JobInfo
package com.njust.pdfmutithread.system.vo;
import com.njust.pdfmutithread.system.CheckJobProcesser;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 类说明:提交给框架执行的工作实体类,工作:表示本批次需要处理的同性质任务(Task)的一个集合
* 注意:返回给用户的不能是引用,防止线程安全事故发生。即不允许用户修改底层框架使用的东西
*/
public class JobInfo<R> {
//区分唯一的工作
private final String jobName;
//工作的任务个数
private final int jobLength;
//这个工作的任务处理器
private final ITaskProcesser<?, ?> taskProcesser;
//成功处理的任务数
private final AtomicInteger successCount;
//已处理的任务数
private final AtomicInteger taskProcesserCount;
//拿结果从头拿,放结果从尾部放 保存工作中每个任务的返回结果,只有处理完成成放进去
private final LinkedBlockingDeque<TaskResult<R>> taskDetailQueue;
//工作的完成保存的时间,超过这个时间从缓存中清除
private final long expireTime;
//阻塞队列不应该由调用者传入,应该内部生成,长度为工作的任务个数
public JobInfo(String jobName, int jobLength,
ITaskProcesser<?, ?> taskProcesser,
long expireTime) {
super();
this.jobName = jobName;
this.jobLength = jobLength;
this.taskProcesser = taskProcesser;
this.successCount = new AtomicInteger(0);
this.taskProcesserCount = new AtomicInteger(0);
this.taskDetailQueue = new LinkedBlockingDeque<>(jobLength);
this.expireTime = expireTime;
}
//返回成功处理的结果数
//不能直接返回 successCount 因为这是一个引用,会导致线程不安全,以下同理
public int getSuccessCount() {
return successCount.get();
}
//返回当前已处理的结果数
public int getTaskProcesserCount() {
return taskProcesserCount.get();
}
//提供工作中失败的次数,为了方便调用者使用
public int getFailCount() {
return taskProcesserCount.get() - successCount.get();
}
//获得工作中每个任务的处理详情
//注意由于阻塞队列是加锁的,所以不存在写的时候读,多线程冲突
public List<TaskResult<R>> getTaskDetail() {
List<TaskResult<R>> taskList = new LinkedList<>();
TaskResult<R> taskResult;
//从阻塞队列中拿任务的结果,反复取,一直取到null为止,说明目前队列中最新的任务结果已经取完,可以不取了
//既然是任务结果,肯定已经处理完成了,所以全部拿完是对的
while ((taskResult = taskDetailQueue.pollFirst()) != null) {
taskList.add(taskResult);
}
return taskList;
}
//从业务应用角度来说,保证最终一致性即可,不需要对方法加锁.
//注意 从jvm内存角度,只有传递进来的参数需要操作的时候才需要新建一个变量接受,即栈变量
public void addTaskResult(TaskResult<R> result, CheckJobProcesser checkJob) {
if (TaskResultType.Success.equals(result.getResultType())) {
successCount.incrementAndGet();
}
taskDetailQueue.addLast(result);
taskProcesserCount.incrementAndGet();
//工作队列满了,放入过期队列
if (taskProcesserCount.get() == jobLength) {
checkJob.putJob(jobName, expireTime);
}
// if (taskProcesserCount.get() >= jobLength) {
// if (result.getReturnValue() == null) {
// System.out.println("chen: " + taskProcesserCount.get());
// } else {
// System.out.println("chen: " + taskProcesserCount.get() + " : " + result.getReturnValue());
// }
//
// }
}
public String getTotalProcess() {
return "Success[" + successCount.get() + "]/Current["
+ taskProcesserCount.get() + "] Total[" + jobLength + "]";
}
public ITaskProcesser<?, ?> getTaskProcesser() {
return taskProcesser;
}
}
TaskResult
package com.njust.pdfmutithread.system.vo;
/**
*
*类说明:任务处理返回结果实体类
*/
public class TaskResult<R> {
//方法本身运行是否正确的结果类型
private final TaskResultType resultType;
//方法的业务结果数据;
private final R returnValue;
//这里放方法失败的原因
private final String reason;
public TaskResult(TaskResultType resultType, R returnValue, String reason) {
super();
this.resultType = resultType;
this.returnValue = returnValue;
this.reason = reason;
}
//方便业务人员使用,这个构造方法表示业务方法执行成功返回的结果
public TaskResult(TaskResultType resultType, R returnValue) {
super();
this.resultType = resultType;
this.returnValue = returnValue;
this.reason = "Success";
}
public TaskResultType getResultType() {
return resultType;
}
public R getReturnValue() {
return returnValue;
}
public String getReason() {
return reason;
}
@Override
public String toString() {
return "TaskResult{" +
"resultType=" + resultType +
", returnValue=" + returnValue +
", reason='" + reason + '\'' +
'}';
}
}
TaskResultType
package com.njust.pdfmutithread.system.vo;
/**
* 类说明:方法本身运行是否正确的结果类型
*/
public enum TaskResultType {
//方法成功执行并返回了业务人员需要的结果
Success,
//方法成功执行但是返回的是业务人员不需要的结果
Failure,
//方法执行抛出了Exception
Exception;
}
CheckJobProcesser
package com.njust.pdfmutithread.system;
import com.njust.pdfmutithread.system.vo.ItemVo;
import java.util.concurrent.DelayQueue;
/**
* 类说明:任务完成后,在一定的时间供查询,之后为释放资源节约内存,需要定期处理过期的任务
*/
public class CheckJobProcesser {
//DelayQueue是一个无界阻塞队列,只有在延迟期满时才能从中提取元素
private static DelayQueue<ItemVo<String>> queue
= new DelayQueue<>();//存放已完成任务等待过期的队列
//单例模式------
private CheckJobProcesser() {
}
private static class ProcesserHolder {
public static CheckJobProcesser processer = new CheckJobProcesser();
}
public static CheckJobProcesser getInstance() {
return ProcesserHolder.processer;
}
//单例模式------
//处理队列中到期任务的实行
private static class FetchJob implements Runnable {
@Override
public void run() {
while (true) {
try {
//拿到已经过期的任务 take()是阻塞方法
ItemVo<String> item = queue.take();
String jobName = item.getDate();
PendingJobPool.getMap().remove(jobName);
System.out.println(jobName + " is out of date,remove from map!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/*任务完成后,放入队列,经过expireTime时间后,从整个框架中移除*/
public void putJob(String jobName, long expireTime) {
//实现名称和到期时间绑定
ItemVo<String> item = new ItemVo<String>(expireTime, jobName);
queue.offer(item);
System.out.println("Job[" + jobName + "已经放入了过期检查缓存,过期时长:" + expireTime);
}
static {
Thread thread = new Thread(new FetchJob());
thread.setDaemon(true);
thread.start();
System.out.println("开启任务过期检查守护线程................");
}
}
PendingJobPool
package com.njust.pdfmutithread.system;
import com.njust.pdfmutithread.system.vo.ITaskProcesser;
import com.njust.pdfmutithread.system.vo.JobInfo;
import com.njust.pdfmutithread.system.vo.TaskResult;
import com.njust.pdfmutithread.system.vo.TaskResultType;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
/**
* 框架的主体类,也是调用者主要使用的类
*/
public class PendingJobPool {
//保守估计
private static final int THREAD_COUNTS =
Runtime.getRuntime().availableProcessors();
//任务队列 注意: 框架尽量避免使用无界队列 防止用户无限制上传
private static BlockingQueue<Runnable> taskQueue
= new ArrayBlockingQueue<>(5000);
//线程池,固定大小,有界队列
private static ExecutorService taskExecutor =
new ThreadPoolExecutor(THREAD_COUNTS, THREAD_COUNTS, 60,
TimeUnit.SECONDS, taskQueue);
//job的存放容器
//G> 与 G extends Object>等同,如List> 与List extends Objext>等同。
private static ConcurrentHashMap<String, JobInfo<?>> jobInfoMap
= new ConcurrentHashMap<>();
private static CheckJobProcesser checkJob
= CheckJobProcesser.getInstance();
public static Map<String, JobInfo<?>> getMap() {
return jobInfoMap;
}
//单例模式------注意:限制用户使用的线程数,防止应用崩掉
private PendingJobPool() {
}
//懒加载式的线程安全
private static class JobPoolHolder {
public static PendingJobPool pool = new PendingJobPool();
}
public static PendingJobPool getInstance() {
return JobPoolHolder.pool;
}
//单例模式------
//对工作中的任务进行包装,提交给线程池使用,并处理任务的结果,写入缓存以供查询
private static class PendingTask<T, R> implements Runnable {
private JobInfo<R> jobInfo;
private T processData;
public PendingTask(JobInfo<R> jobInfo, T processData) {
super();
this.jobInfo = jobInfo;
this.processData = processData;
}
//告诉编译器忽略指定的警告,不用在编译完成后出现警告信息。
@SuppressWarnings("unchecked")
@Override
public void run() {
R r = null;
ITaskProcesser<T, R> taskProcesser =
(ITaskProcesser<T, R>) jobInfo.getTaskProcesser();
TaskResult<R> result = null;
//用户的代码是最不可信的,所以防止用户程序抛出异常,需要try catch处理
try {
//调用业务人员实现的具体方法
result = taskProcesser.taskExecute(processData);
//要做检查,防止开发人员处理不当
if (result == null) {
result = new TaskResult<R>(TaskResultType.Exception, r,
"result is null");
}
if (result.getResultType() == null) {
if (result.getReason() == null) {
result = new TaskResult<R>(TaskResultType.Exception, r, "reason is null");
} else {
result = new TaskResult<R>(TaskResultType.Exception, r,
"result is null,but reason:" + result.getReason());
}
}
} catch (Exception e) {
// e.printStackTrace();
result = new TaskResult<R>(TaskResultType.Exception, r,
e.getMessage());
} finally {
jobInfo.addTaskResult(result, checkJob);
}
}
}
//根据工作名称检索工作
@SuppressWarnings("unchecked")
private <R> JobInfo<R> getJob(String jobName) {
JobInfo<R> jobInfo = (JobInfo<R>) jobInfoMap.get(jobName);
if (null == jobInfo) {
throw new RuntimeException(jobName + "是个非法任务、或该任务已经过期!");
}
return jobInfo;
}
//调用者提交工作中的任务
public <T, R> void putTask(String jobName, T t) {
JobInfo<R> jobInfo = getJob(jobName);
PendingTask<T, R> task = new PendingTask<>(jobInfo, t);
taskExecutor.execute(task);
}
//调用者注册工作,如工作名,任务的处理器等等,名称唯一,相当于ID
public <R> void registerJob(String jobName, int jobLength,
ITaskProcesser<?, ?> taskProcesser, long expireTime) {
JobInfo<R> jobInfo = new JobInfo<>(jobName, jobLength, taskProcesser, expireTime);
// 不能使用put,防止任务在处理的时候,用户修改map,导致处理异常
if (jobInfoMap.putIfAbsent(jobName, jobInfo) != null) {
throw new RuntimeException(jobName + "已经注册了!");
}
}
//获得每个任务的处理详情
public <R> List<TaskResult<R>> getTaskDetail(String jobName) {
JobInfo<R> jobInfo = getJob(jobName);
return jobInfo.getTaskDetail();
}
//获得工作的整体处理进度
public <R> String getTaskProgess(String jobName) {
JobInfo<R> jobInfo = getJob(jobName);
return jobInfo.getTotalProcess();
}
}
PDFTaskMutiThread
package com.njust.pdfmutithread.user.task;
import com.itextpdf.text.DocumentException;
import com.njust.pdfmutithread.system.vo.ITaskProcesser;
import com.njust.pdfmutithread.system.vo.TaskResult;
import com.njust.pdfmutithread.system.vo.TaskResultType;
import com.njust.pdfmutithread.user.PDFService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* @author Chen
* @version 1.0
* @date 2020/3/26 22:06
* @description: 多线程版生成PDF
*/
@Component
public class PDFTaskMutiThread implements ITaskProcesser<String, Integer> {
@Autowired
private PDFService pdfService;
@Override
public TaskResult<Integer> taskExecute(String data) {
createOne(data + ".pdf");
return new TaskResult<>(TaskResultType.Success, Integer.valueOf(data));
}
public void createOne(String fileName) {
//需要填充的数据
Map<String, Object> data = new HashMap<>(16);
String content = pdfService.freeMarkerRender(data, pdfService.getHtml());
// 当content 为null时 ,不停尝试
while (content == null) {
content = pdfService.freeMarkerRender(data, pdfService.getHtml());
}
String temp = pdfService.getDest() + fileName;
//创建pdf
try {
pdfService.createPdf(content, temp);
} catch (IOException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
}
}
}
修改PDFServiceTest
类,添加多线程版的测试方法。完整代码如下:
package com.njust.pdfmutithread.user;
import com.njust.pdfmutithread.system.PendingJobPool;
import com.njust.pdfmutithread.system.vo.TaskResult;
import com.njust.pdfmutithread.user.task.PDFTaskMutiThread;
import com.njust.pdfmutithread.user.task.PDFTaskSingThread;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
/**
* @author Chen
* @version 1.0
* @date 2020/3/26 21:55
* @description:
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class PDFServiceTest {
@Autowired
private PDFTaskSingThread pdfTaskSingThread;
public static void printTime(long start, long end) {
long spendTime = end - start;
System.out.println("spendTime: " + spendTime);
}
@Test
public void createOne() {
long start = System.currentTimeMillis();
pdfTaskSingThread.createOne();
long end = System.currentTimeMillis();
printTime(start, end);
}
// JOB_LENGTH:260 spendTime: 17310
// JOB_LENGTH:512 spendTime: 30660
// JOB_LENGTH:1024 spendTime: 59573
@Test
public void createManyWithOneThread() {
long start = System.currentTimeMillis();
pdfTaskSingThread.createManyWithOneThread(JOB_LENGTH);
long end = System.currentTimeMillis();
printTime(start, end);
}
private final static String JOB_NAME = "生成PDF";
private final static int JOB_LENGTH = 1024;
@Autowired
private PDFTaskMutiThread pdfTaskMutiThread;
//查询任务进度的线程
private static class QueryResult implements Runnable {
private PendingJobPool pool;
public QueryResult(PendingJobPool pool) {
super();
this.pool = pool;
}
@Override
public void run() {
int num = 0;
List<TaskResult<String>> taskDetail;
try {
while (num < JOB_LENGTH) {
taskDetail = pool.getTaskDetail(JOB_NAME);
if (!taskDetail.isEmpty()) {
num += taskDetail.size();
}
}
} catch (Exception e) {
}
end_chen = System.currentTimeMillis();
PDFServiceTest.printTime(start_chen, end_chen);
}
}
private static long start_chen;
private static long end_chen;
// JOB_LENGTH:260 spendTime: 17599
// JOB_LENGTH:512 spendTime: 33531
// JOB_LENGTH:1024 spendTime: 56144
@Test
public void testPDFTaskMutiThread() throws InterruptedException {
start_chen = System.currentTimeMillis();
//拿到框架的实例
PendingJobPool pool = PendingJobPool.getInstance();
//注册job
pool.registerJob(JOB_NAME, JOB_LENGTH, pdfTaskMutiThread, 1000 * 50);
// 假定用户不按套路出牌
for (int i = 0; i < JOB_LENGTH; i++) {
//依次推入Task
pool.putTask(JOB_NAME, String.valueOf(i));
}
Thread t = new Thread(new QueryResult(pool));
t.start();
t.join();
}
}
可以发现当生成的PDF较多时,多线程占优势。但是苦逼的我电脑配置实在是太差了。能跑出这个效果已经谢天谢帝了。
多线程版的时序图,如上图所示。
多线程生成PDF会报异常,但是工作框架具有完美的异常处理能力。即使有异常,但是依然可以正确生成PDF。但是为什么出现异常还没有搞明白。等我搞明白了,再更新一下。睡觉了。。。各位小伙伴晚安!!!
多线程在IO密集型任务明显占有优势。但是实现起来真的烧脑啊。各位读者有什么问题,欢迎评论交流!