如何使用Java(Android)退出包含可调用ExecutorService多线程的循环

如何使用Java(Android)退出包含可调用ExecutorService多线程的循环,java,android,multithreading,callable,Java,Android,Multithreading,Callable,我有一个大的plist(Xml)文件,我用SAX(ddplistlibrary)解析它。因为它是一个用于解析的大文件,并且出于性能原因,我必须使用多线程,我的目标是获得与plist文件中键的确切数量相同的线程的确切数量,我的意思是,对于plist中的每个键,单个线程搜索值并将其与url进行比较,如果键和url相等,然后返回键的值,否则返回null并跳过并取消线程(该值是html内容的标题,键是存储在plist中的路径,url是用户单击并在Android WebView的onPageFinishe

我有一个大的plist(Xml)文件,我用SAX(ddplistlibrary)解析它。因为它是一个用于解析的大文件,并且出于性能原因,我必须使用多线程,我的目标是获得与plist文件中键的确切数量相同的线程的确切数量,我的意思是,对于plist中的每个键,单个线程搜索值并将其与url进行比较,如果键和url相等,然后返回键的值,否则返回null并跳过并取消线程(该值是html内容的标题,键是存储在plist中的路径,url是用户单击并在Android WebView的onPageFinished中捕获的链接的url)。若有人能告诉我上述目标,我会非常感激,因为代码遗漏了什么

在my WebFragment(android.support.v4.app.Fragment)中,在onPageFinished中:

import com.dd.plist.NSDictionary;
import com.dd.plist.NSObject;
import com.dd.plist.PropertyListParser;

...
try {
                                is = getResources().openRawResource(R.raw.title);
                                rootDict = (NSDictionary) PropertyListParser.parse(is);
                                dict = new LinkedHashMap<>();
                                dict = rootDict.getHashMap();
                                ExecutorService executor = Executors.newFixedThreadPool(rootDict.size());
                                Future<String> future;
                                String myStr = null;
                                String key;
                                NSObject value;

                                for (Map.Entry<String, NSObject> entry : dict.entrySet()) {
                                    key = entry.getKey();
                                    value = entry.getValue();
// following line is refer to WebFragment (line 285 where logs complain and crash because of the memory
                                    future = executor.submit(new ParsePlistThread(key, value, url.substring(32).toString()));
                                    myStr = future.get();
                                    if (myStr != null && !myStr.isEmpty()) {
                                        break;
                                    } else {
                                        //future.cancel(true);
                                    }
                                }
                                executor.shutdown();   

                                if (myStr != null) {

                                    if (numTab == 0) {
                                        titleTextView.setText(myStr);
                                    } 
                            } catch (Exception ex) {
                                //Handle exceptions...
                            } 
你的方法有问题吗 除了将非常小的任务放入其他线程的概念性问题(您将花费比实际计算更多的时间传递信息),此代码有两个主要问题:

1) 你得到的实际错误。此错误是由于堆栈内存不足造成的。Java内存分为多个区域,其中最大也是最常见的是“堆内存”。这是(几乎)所有对象所在的位置。一个鲜为人知的区域是“堆栈内存”。这是线程获取内存以存储当前状态、堆栈跟踪、本地(方法)变量等的地方。创建
线程时,会从该空间为其堆栈分配一些固定内存。如果创建的线程太多,它将耗尽,并抛出一个错误,例如您遇到的错误

解决方案-重用您的线程

执行器具有内置的功能,可以在线程完成任务时重用线程。下面将对此进行详细介绍。一般来说,在CPU中拥有比逻辑核更多的线程不会提高速度

2) 实际上你并没有同时做任何事情。在循环中,您将任务提交给
执行者
(执行者.submit
方法),然后等待任务完成(
future.get
),然后转到下一行。因此,在创建新任务之前,您正在等待当前任务完成!您将不会有两个任务与此安排并行运行

这里的最后一点是,您不应该依赖多线程来加速文件处理。瓶颈几乎总是在读取文件。很可能是你对它做了一些愚蠢的事情,使它变慢了


多线程处理做得更好。 评论中提到,在值得的情况下,看看如何解决这些错误可能是有用的。下面是修复并发问题的一种半天真的方法

第一件事是首先限制你需要的线程。如果你在一个四核桌面上运行超线程,我建议你使用6个线程,或者仅仅是一个工作窃取池。我不确定Android的好数字是多少,但它肯定比你“非常大的文件”中的行数低

这样,当您的任务数超过可用线程数时(因此,除非您的线程是I/O绑定的),新任务将排队等待,直到现有线程可用为止,而不是生成更多线程

下一个问题实际上是将所有任务放入执行器以尽快执行,而不是按顺序执行。为此,您必须跟踪您所创建的未来(注意,我还删除了每个循环上的子字符串,因为URL在调用之间似乎没有变化,所以您可以对其进行预计算。这是一件普通的事情-不要在循环中重复您可以做一次的工作!)

List tasks=new ArrayList();
for(Map.Entry:dict.entrySet()){
key=entry.getKey();
value=entry.getValue();
添加(executor.submit(新的ParsePlistThread(key,value,url));
}
既然您已经提交了所有任务(我将再次重申,在如此大量的情况下使用如此小的任务通常会适得其反),那么您需要收集结果。做这件事相当简单,只需重复你的未来

String result;
for (Future<String> fut : tasks) {
    String taskResult = fut.get();
    if (taskResult != null && !taskResult.isEmpty()) {
        result = taskResult;
        break;
    }
}
字符串结果;
for(未来未来未来:任务){
字符串taskResult=fut.get();
if(taskResult!=null&&!taskResult.isEmpty()){
结果=任务结果;
打破
}
}
您的方法与此方法之间有一个主要区别——如果您的方法发现了结果,则不会继续解析。在这种情况下,您只需对尚未访问的期货使用
future.cancel
,即可实现这一点。我把密码留给你。一般来说,这是比较困难的,因为这将涉及到线程间的通信(您必须向另一个线程发出信号以正常停止其执行,这可能并不简单)


一句忠告——在试图获得更高速度的同时开始学习多线程技术并不是很有成效。它有许多微妙之处(上面甚至没有提到两个魔鬼-语句重新排序和内存可见性),正确快速地完成它们是一个相当大的挑战!尝试并行地做一些事情要好得多,但不一定要更快,但要做到正确。当您对正确创建并行进程感到满意时,您可以考虑将其加快。

您的方法存在问题 除了将非常小的任务放入其他线程的概念性问题(您将花费比实际计算更多的时间传递信息),此代码有两个主要问题:

1) 你得到的实际错误。此错误是由于堆栈内存不足造成的。Java内存分为多个区域,其中最大和最常见的是kno
E/art: Throwing OutOfMemoryError "pthread_create (1040KB stack) failed: Try again"
08-19 09:52:50.328 28749-28749/ca.ccohs.oshanswers E/AndroidRuntime: FATAL EXCEPTION: main
                                                                     Process: XXX, PID: 28749
                                                                     java.lang.OutOfMemoryError: pthread_create (1040KB stack) failed: Try again
                                                                         at java.lang.Thread.nativeCreate(Native Method)
                                                                         at java.lang.Thread.start(Thread.java:1063)
                                                                         at java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:920)
                                                                         at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1327)
                                                                         at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:103)
                                                                         at ca.ccohs.oshanswers.ui.WebFragment$3.onPageFinished(WebFragment.java:285)
                                                                         at com.android.webview.chromium.WebViewContentsClientAdapter.onPageFinished(WebViewContentsClientAdapter.java:531)
                                                                         at org.chromium.android_webview.AwContentsClientCallbackHelper$MyHandler.handleMessage(AwContentsClientCallbackHelper.java:188)
                                                                         at android.os.Handler.dispatchMessage(Handler.java:102)
                                                                         at android.os.Looper.loop(Looper.java:145)
                                                                         at android.app.ActivityThread.main(ActivityThread.java:6117)
                                                                         at java.lang.reflect.Method.invoke(Native Method)
                                                                         at java.lang.reflect.Method.invoke(Method.java:372)
                                                                         at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1399)
                                                                         at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1194)
08-19 09:52:50.343 2850-29945/? E/android.os.Debug: ro.product_ship = true
08-19 09:52:50.343 2850-29945/? E/android.os.Debug: ro.debug_level = 0x4f4c
ExecutorService executor = Executors.newFixedThreadPool(6);
ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
List<Future<String>> tasks = new ArrayList<>();
for (Map.Entry<String, NSObject> entry : dict.entrySet()) {
    key = entry.getKey();
    value = entry.getValue();
    tasks.add(executor.submit(new ParsePlistThread(key, value, url)));
}
String result;
for (Future<String> fut : tasks) {
    String taskResult = fut.get();
    if (taskResult != null && !taskResult.isEmpty()) {
        result = taskResult;
        break;
    }
}