优化点:
- 模板预加载到内存(字节缓存),每次从内存中读取来生成模板,线程安全且减少IO
- 生成word后放到字节缓存中,避免中间文件IO
- 批量任务使用IO密集型线程池
核心代码:
import com.deepoove.poi.XWPFTemplate;
import org.docx4j.Docx4J;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import java.util.*;
import java.io.*;
import java.util.concurrent.*;
public class ConcurrentDocGenerator {
private static final ExecutorService executor = new ThreadPoolExecutor(
4,
Runtime.getRuntime().availableProcessors() * 2,
60L, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100),
new CustomThreadFactory("doc-gen"),
new ThreadPoolExecutor.CallerRunsPolicy()
);
private static final byte[] templateBytes;
static {
try (InputStream is = ConcurrentDocGenerator.class.getResourceAsStream("/template.docx")) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) > 0) {
bos.write(buffer, 0, len);
}
templateBytes = bos.toByteArray();
} catch (IOException e) {
throw new RuntimeException("加载模板失败", e);
}
}
private static class DocGenTask implements Callable<byte[]> {
private final Map<String, Object> data;
DocGenTask(Map<String, Object> data) {
this.data = new HashMap<>(data);
}
@Override
public byte[] call() throws Exception {
try (InputStream is = new ByteArrayInputStream(templateBytes);
XWPFTemplate template = XWPFTemplate.compile(is).render(data)) {
ByteArrayOutputStream wordBos = new ByteArrayOutputStream();
template.write(wordBos);
try (InputStream pdfIs = new ByteArrayInputStream(wordBos.toByteArray())) {
WordprocessingMLPackage pkg = WordprocessingMLPackage.load(pdfIs);
ByteArrayOutputStream pdfBos = new ByteArrayOutputStream();
Docx4J.toPDF(pkg, pdfBos);
return pdfBos.toByteArray();
}
}
}
}
public List<Future<byte[]>> batchGenerate(List<Map<String, Object>> dataList) {
List<Future<byte[]>> futures = new ArrayList<>(dataList.size());
for (Map<String, Object> data : dataList) {
futures.add(executor.submit(new DocGenTask(data)));
}
return futures;
}
private static class CustomThreadFactory implements ThreadFactory {
private final String namePrefix;
private final AtomicInteger threadNumber = new AtomicInteger(1);
CustomThreadFactory(String poolName) {
namePrefix = poolName + "-thread-";
}
public Thread newThread(Runnable r) {
Thread t = new Thread(r, namePrefix + threadNumber.getAndIncrement());
t.setDaemon(false);
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
public static void shutdown() {
executor.shutdown();
try {
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
}
public static void main(String[] args) throws Exception {
List<Map<String, Object>> testData = new ArrayList<>();
for (int i = 0; i < 10; i++) {
Map<String, Object> data = new HashMap<>();
data.put("title", "文档-" + i);
data.put("content", "这是第" + i + "个并发生成的文档");
testData.add(data);
}
ConcurrentDocGenerator generator = new ConcurrentDocGenerator();
List<Future<byte[]>> results = generator.batchGenerate(testData);
for (int i = 0; i < results.size(); i++) {
try {
byte[] pdfBytes = results.get(i).get(30, TimeUnit.SECONDS);
try (FileOutputStream fos = new FileOutputStream("output-" + i + ".pdf")) {
fos.write(pdfBytes);
}
System.out.println("文档" + i + "生成成功");
} catch (TimeoutException e) {
System.err.println("文档" + i + "生成超时");
} catch (ExecutionException e) {
System.err.println("文档" + i + "生成失败: " + e.getCause().getMessage());
}
}
shutdown();
}
}