我已经解决了冲突问题,但是我发现多线程并没有缩短时间啊,和我设想的不一样呢?
单线程和双线程、四线程运行的时间都一样,想哭了!
我的目标是5s左右识别一张1920*1080大小的图片,这样我才能用到识别结果,否则光是识别图片就用了20来秒太慢了。
我在学习多线程时仿照例子把单线程改为多线程,却始终无法成功。
在示例中是可以实现多线程结果共享的,但是在实战例子中就不行了。小女子刚学多线程,求指点一下具体怎么改。
以下是例子:
class hello implements Runnable {
public void run() {
for ( int i = 0 ; i < 7 ; i++) {
if (count > 0 ) {
System.out.println( Thread.currentThread().getName() + "count= " + count--);
}
}
}
public static void main(String[] args) {
hello he= new hello();
new Thread(he, "A").start();
new Thread(he, "B").start();
}
private int count = 5 ;
}
例子结果:
这是我在修改的实战例子:
public class imageUrl implements Runnable {
public void run(){
BytePointer outtext;
TessBaseAPI api = new TessBaseAPI();
api.Init("tessdata", "chi_sim") ;//chi_sim在tessdata中位置
File file = new File("tupian/3.png");//图片地址
PIX image = pixRead(file.getAbsolutePath());
api.SetImage(image);
int[] blockIds = {};
long starttime = System.currentTimeMillis();
BOXA boxes = api.GetComponentImages(RIL_WORD, true, null, blockIds);
for (int i = 0; i < boxes.n(); i++) {
BOX box = boxes.box(i);
api.SetRectangle(box.x(), box.y(), box.w(), box.h());
outtext = api.GetUTF8Text();
String ocrresult = outtext.getString();
//去空格
ocrresult = ocrresult.replace(" ","");
int conf = api.MeanTextConf();
String boxinformation = String.format("Box[%d]:x=%d,y=%d,w=%d,h=%d,confidence:%d,text:%s", i, box.x(),
box.y(), box.w(), box.h(), conf, ocrresult);
System.out.println(boxinformation);
outtext.deallocate();
}
api.End();
pixDestroy(image);
long invertaltime = System.currentTimeMillis() - starttime;
System.out.println("识别用时:" + invertaltime);
}
public static void main(String[] args) {
imageUrl ds1 = new imageUrl();
imageUrl ds2 = new imageUrl();
new Thread(ds1,"A").start();
new Thread(ds2,"B").start();
}
}
这是实例运行结果:
这个结果不是我想要的,我想要的是BOX识别多线程共享,而不是识别两次。
根据你的代码进行修改的
import org.bytedeco.javacpp.BytePointer;
import org.bytedeco.leptonica.BOX;
import org.bytedeco.leptonica.BOXA;
import org.bytedeco.leptonica.PIX;
import org.bytedeco.tesseract.TessBaseAPI;
import java.io.File;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import static org.bytedeco.leptonica.global.lept.pixDestroy;
import static org.bytedeco.leptonica.global.lept.pixRead;
import static org.bytedeco.tesseract.global.tesseract.RIL_WORD;
public class imageUrl implements Runnable {
private AtomicInteger index = new AtomicInteger(0);
private BOXA boxes;
private TessBaseAPI api;
private CountDownLatch latch;
public AtomicBoolean flag = new AtomicBoolean(true);
public imageUrl(){}
public imageUrl(CountDownLatch latch){
this.latch = latch;
init();
}
/**
* 初始化处理
*/
public void init(){
api = new TessBaseAPI();
//chi_sim在tessdata中位置
api.Init("tessdata", "chi_sim") ;
//图片地址
File file = new File("tupian/3.png");
PIX image = pixRead(file.getAbsolutePath());
api.SetImage(image);
int[] blockIds = {};
boxes = api.GetComponentImages(RIL_WORD, true, null, blockIds);
}
/**
* 执行任务
*/
@Override
public void run(){
long startTime = System.currentTimeMillis() ;
int num = 0;
//cas 原子操作
while ( (num =index.getAndAdd(1)) < boxes.n()) {
BOX box = boxes.box(index.get());
if (box == null){
continue;
}
int conf;
String ocrresult;
synchronized(imageUrl.class){
api.SetRectangle(box.x(), box.y(), box.w(), box.h());
BytePointer outtext = api.GetUTF8Text();
conf = api.MeanTextConf();
ocrresult = outtext.getString();
outtext.deallocate();
}
//去空格
ocrresult = ocrresult.replace(" ","");
String boxinformation = String.format("Box[%d]:x=%d,y=%d,w=%d,h=%d,confidence:%d,text:%s", num, box.x(),
box.y(), box.w(), box.h(), conf, ocrresult);
System.out.println(boxinformation);
}
//结束 防止重复结束
if (flag.getAndSet(false)){
end();
}
long totalTime = System.currentTimeMillis() - startTime;
System.out.println("====》识别用时:" + totalTime);
latch.countDown();
}
/**
* 结束处理
*/
public void end(){
api.End();
PIX image = new PIX();
pixDestroy(image);
System.out.println("结束释放资源");
}
}
class Test{
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(2);
imageUrl ds = new imageUrl(latch);
new Thread(ds,"A").start();
new Thread(ds,"B").start();
latch.await();
}
}
如果是为了提升识别速度,可以做数据切割识别,多个线程分不同的起点开始识别,最后可以合并识别结果,
public static void main(String[] args) throws ExecutionException, InterruptedException {
long l = System.currentTimeMillis();
// 切分数
int submitCount = 3;
ExecutorService executorService = Executors.newFixedThreadPool(submitCount);
List<Future<List<String>>> futures = new ArrayList<>();
for (int i = 0; i < submitCount; i++) {
int finalI = i;
Future<List<String>> submit = executorService.submit(new Callable<List<String>>() {
@Override
public List<String> call() throws Exception {
TessBaseAPI api = new TessBaseAPI();
api.Init("tessdata", "chi_sim");//chi_sim在tessdata中位置
File file = new File("tupian/3.png");//图片地址
PIX image2 = pixRead(file.getAbsolutePath());
api.SetImage(image2);
int[] blockIds = {};
BOXA boxes = api.GetComponentImages(RIL_WORD, true, null, blockIds);
int nalloc = boxes.nalloc();
List<String> list = new ArrayList<>();
int index = 0;
while (index < nalloc / submitCount) {
BOX box = boxes.box(nalloc / submitCount * finalI + index);
api.SetRectangle(box.x(), box.y(), box.w(), box.h());
BytePointer outtext = api.GetUTF8Text();
String ocrresult = outtext.getString();
//去空格
ocrresult = ocrresult.replace(" ", "");
int conf = api.MeanTextConf();
String boxinformation = String.format("Box[%d]:x=%d,y=%d,w=%d,h=%d,confidence:%d,text:%s", index, box.x(),
box.y(), box.w(), box.h(), conf, ocrresult);
list.add(boxinformation);
outtext.deallocate();
index++;
}
api.End();
PIX image = new PIX();
pixDestroy(image);
return list;
}
});
futures.add(submit);
}
for (Future<List<String>> future : futures) {
List<String> list1 = future.get();
for (String s : list1) {
System.out.println(s);
}
}
executorService.shutdown();
System.out.println(System.currentTimeMillis() - l);
}
可以比较一下下面两段代码,一个是你写的,一个是例子,会发现例子是将同一个对象传给了两个线程,你的例子是将两个对象,传给了两个线程。就相当于,在多线程中处理每个线程各自的任务,而不是将同一个对象让多个线程进行处理。按照这个思路,修改一下你的代码就可以了
public static void main(String[] args) {
imageUrl ds1 = new imageUrl();
imageUrl ds2 = new imageUrl();
new Thread(ds1,"A").start();
new Thread(ds2,"B").start();
}
public static void main(String[] args) {
hello he= new hello();
new Thread(he, "A").start();
new Thread(he, "B").start();
}
你的两个runnable做的同一件事情,并没有起到该有的作用
你应该把
多线程处理循环可以考虑用线程池,?%ra=linkhttps://blog.csdn.net/m0_37729339/article/details/113179192?spm=1001.2014.3001.5502
public static void main(String[] args) {
imageUrl ds1 = new imageUrl();
imageUrl ds2 = new imageUrl();
new Thread(ds1,"A").start();
new Thread(ds2,"B").start();
}
就比如你这段代码可以改为
imageUrl ds1 = new imageUrl();
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.submit(ds1);
主要是共享资源和线程间协调的问题
以下实现不知道行不行:
public class ImageUrl implements Runnable {
ConcurrentLinkedDeque<BOX> deque = new ConcurrentLinkedDeque<>();
AtomicInteger atomicInteger;
AtomicInteger timer = new AtomicInteger(0);
TessBaseAPI api;
PIX image;
public ImageUrl(){
api = new TessBaseAPI();
api.Init("tessdata", "chi_sim") ;//chi_sim在tessdata中位置
File file = new File("tupian/3.png");//图片地址
image = pixRead(file.getAbsolutePath());
api.SetImage(image);
int[] blockIds = {};
BOXA boxes = api.GetComponentImages(RIL_WORD, true, null, blockIds);
for (int i = 0; i < boxes.n(); i++) {
BOX box = boxes.box(i);
deque.add(box);
}
atomicInteger = new AtomicInteger(blockIds.length);
}
public void run(){
BytePointer outtext;
long starttime = System.currentTimeMillis();
while (true) {
int i = 0;
synchronized (Object.class) {
BOX box = deque.pop();
if (box == null && atomicInteger.get() == 0) {
api.End();
pixDestroy(image);
break;
}
if (box == null) {
continue;
}
i = atomicInteger.decrementAndGet();
}
api.SetRectangle(box.x(), box.y(), box.w(), box.h());
outtext = api.GetUTF8Text();
String ocrresult = outtext.getString();
//去空格
ocrresult = ocrresult.replace(" ","");
int conf = api.MeanTextConf();
String boxinformation = String.format("Box[%d]:x=%d,y=%d,w=%d,h=%d,confidence:%d,text:%s", i, box.x(),
box.y(), box.w(), box.h(), conf, ocrresult);
System.out.println(boxinformation);
outtext.deallocate();
}
long invertaltime = System.currentTimeMillis() - starttime;
timer.addAndGet((int) invertaltime);
}
public static void main(String[] args) {
ImageUrl ds = new ImageUrl();
new Thread(ds,"A").start();
new Thread(ds,"B").start();
System.out.println("识别用时:" + ds.timer.get());
}
}
我把整个idea项目压缩包放在网盘里了可以帮我看看具体怎么回事吗?完整的追赏[](链接:https://pan.baidu.com/s/1NITCu74S7vsGUNWq4qog8w
提取码:0000)
我也不会,还没学到多线程,我倒是仔细看了一下实例,应该是把for那段代码run()吧,而不是整体run()了,你的代码相当于把图片识别整体分入两个线程跑了两遍。得到两次结果也就不怪了
有没有想过,多线程包的是分支任务,而不是整个工程。
区别是这样的:
多个线程干一件事,每个人都会干一遍。
多个线程分发一堆事,各自干各自的部分。这也是楼主的目标。
多线程一般需要配合锁来实现相关的共享的,学习锁相关的知识会对你的进一步理解多线程是非常有帮助的