我目前使用此代码从PDF文件中提取文本:
String text = new PDFTextStripper().getText(PDDocument.load(content));
我需要在多线程应用程序中运行它,PDFTextStripper
不是线程安全的。我想在每个帖子中只使用ThreadLocal
初始化PDFTextStripper
一次。我尝试如下:
import ammonite.ops._
import org.apache.pdfbox.pdmodel.PDDocument
import org.apache.pdfbox.text.PDFTextStripper
(0 until 100).par.foreach { i =>
println(s"#$i. START.")
val pdfTextStripper = new java.lang.ThreadLocal[PDFTextStripper] {
override def initialValue: PDFTextStripper = {
println(s"new PDFTextStripper. #$i")
new PDFTextStripper
}
}
val content = read.bytes! Path(FilePath(s"/data/file_$i.pdf"), pwd)
val doc = PDDocument.load(content)
println(pdfTextStripper.get.getText(doc))
}
我有100个文档,我正在使用(0 until 100).par
,它正在使用10个线程,我想。因此,文本new PDFTextStripper. #$i
应该只显示10次,但它会出现100次。 PDFTextStripper
未被重复使用。为什么呢?
答案 0 :(得分:2)
您应该只将ThreadLocal
实例化一次,然后在并行计算中使用它。每个调用get()
的线程都会创建一个PDFTextStripper
,然后重复使用它:
val pdfTextStripper = new java.lang.ThreadLocal[PDFTextStripper] {
override def initialValue: PDFTextStripper = {
println(s"new PDFTextStripper") // this will be printed once per thread
new PDFTextStripper
}
}
(0 until 100).par.foreach { i =>
println(s"#$i. START.")
val content = read.bytes! Path(FilePath(s"/data/file_$i.pdf"), pwd)
val doc = PDDocument.load(content)
println(pdfTextStripper.get.getText(doc))
}
一些直觉:将ThreadLocal
个实例视为 maps 是有用的,其中键是线程,值是由initialValue
获得的延迟评估对象。所以 - 您只需要一个这样的地图,您不希望在每次迭代时重新创建地图(从而失去对前一个地图的访问权限)。